> ## Documentation Index
> Fetch the complete documentation index at: https://hyperscape-ai-mintlify-docs-update.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Setup

> CDN, PostgreSQL, and container management

## Overview

Hyperscape uses Docker for:

* **Asset CDN**: nginx serving game assets
* **PostgreSQL**: Database for player data
* **Development services**: Local infrastructure

## Prerequisites

* [Docker Desktop](https://docker.com/products/docker-desktop) (macOS/Windows)
* Or Docker Engine on Linux: `apt install docker.io`

## CDN Container

### Start CDN

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bun run cdn:up
```

This starts an nginx container serving assets from `packages/server/world/assets/` on port 8080.

### Stop CDN

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
bun run cdn:down
```

### Automatic Start

The CDN starts automatically with `bun run dev`. Only run manually if using services separately.

## PostgreSQL Container

PostgreSQL starts automatically when the server runs via Docker Compose.

### Configuration

Default connection:

```
Host: localhost
Port: 5432
Database: hyperscape
User: postgres
Password: postgres
```

### Manual Control

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
cd packages/server
docker-compose up postgres    # Start PostgreSQL
docker-compose down postgres  # Stop PostgreSQL
```

## Docker Compose

The server package includes `docker-compose.yml`:

```yaml theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
services:
  postgres:
    image: postgres:15
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: hyperscape
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    volumes:
      - postgres-data:/var/lib/postgresql/data

  cdn:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./world/assets:/usr/share/nginx/html:ro
```

## Production Docker Images

### Server Image

The server Dockerfile includes all dependencies for production deployment with Bun 1.3.10 and multi-service support:

```dockerfile theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# ─── Builder ────────────────────────────────────────────────────────────────

FROM oven/bun:1.3.10-debian AS builder

RUN apt-get update && apt-get install -y \
    python3 make g++ pkg-config \
    libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev \
    ca-certificates git \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

ENV CI=true \
    SKIP_ASSETS=true \
    HYPERSCAPE_SKIP_BROWSER_INSTALL=true

# Root manifests
COPY package.json bun.lock turbo.json tsconfig.json ./

# Workspace package manifests
COPY packages/physx-js-webidl/package.json ./packages/physx-js-webidl/
COPY packages/decimation/package.json      ./packages/decimation/
COPY packages/impostors/package.json       ./packages/impostors/
COPY packages/procgen/package.json         ./packages/procgen/
COPY packages/shared/package.json          ./packages/shared/
COPY packages/plugin-hyperscape/package.json ./packages/plugin-hyperscape/
COPY packages/web3/package.json            ./packages/web3/
COPY packages/server/package.json          ./packages/server/
COPY packages/client/package.json          ./packages/client/

# Install hook scripts + PhysX WASM prebuilts
COPY scripts ./scripts
COPY packages/client/public/web/physx-js-webidl.js  ./packages/client/public/web/
COPY packages/client/public/web/physx-js-webidl.wasm ./packages/client/public/web/

# Workspace sources
COPY packages/physx-js-webidl    ./packages/physx-js-webidl
COPY packages/decimation         ./packages/decimation
COPY packages/impostors          ./packages/impostors
COPY packages/procgen            ./packages/procgen
COPY packages/shared             ./packages/shared
COPY packages/plugin-hyperscape  ./packages/plugin-hyperscape
COPY packages/web3               ./packages/web3
COPY packages/server             ./packages/server
COPY packages/client             ./packages/client

# better-sqlite3's node-gyp build segfaults under QEMU cross-compilation
# The project uses bun:sqlite / PostgreSQL, so it's safe to remove
RUN bun -e " \
  const fs = require('fs'); \
  for (const f of ['packages/shared/package.json', 'package.json']) { \
    const p = JSON.parse(fs.readFileSync(f)); \
    delete p.dependencies?.['better-sqlite3']; \
    delete p.devDependencies?.['better-sqlite3']; \
    fs.writeFileSync(f, JSON.stringify(p, null, 2)); \
  }"

RUN bun install --trust

# Download game asset manifests (bypass CDN reliance on boot)
RUN rm -rf packages/server/world/assets && bun scripts/ensure-assets.mjs

# Build workspace packages in dependency order
RUN cd /app/packages/physx-js-webidl && bun run build && \
    cd /app/packages/decimation && bun run build && \
    cd /app/packages/impostors && bun run build && \
    cd /app/packages/procgen && (bun run build || true) && \
    cd /app/packages/shared && bun run build && \
    cd /app/packages/plugin-hyperscape && bun run build && \
    cd /app/packages/web3 && bun run build && \
    cd /app/packages/server && bun run build && \
    cd /app/packages/client && NODE_OPTIONS='--max-old-space-size=4096' bun run build:cf

# ─── Runtime ────────────────────────────────────────────────────────────────

FROM oven/bun:1.3.10-debian AS runtime

RUN apt-get update && apt-get install -y \
    libcairo2 libpango-1.0-0 libpangocairo-1.0-0 \
    libjpeg62-turbo libgif7 librsvg2-2 \
    ca-certificates curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

ENV NODE_ENV=production \
    PORT=5555 \
    SKIP_ASSETS=true \
    HYPERSCAPE_SKIP_BROWSER_INSTALL=true

# Package manifests from builder (has better-sqlite3 stripped)
COPY --from=builder /app/package.json /app/bun.lock ./
COPY --from=builder /app/packages/physx-js-webidl/package.json ./packages/physx-js-webidl/
COPY --from=builder /app/packages/decimation/package.json      ./packages/decimation/
COPY --from=builder /app/packages/impostors/package.json       ./packages/impostors/
COPY --from=builder /app/packages/procgen/package.json         ./packages/procgen/
COPY --from=builder /app/packages/shared/package.json          ./packages/shared/
COPY --from=builder /app/packages/plugin-hyperscape/package.json ./packages/plugin-hyperscape/
COPY --from=builder /app/packages/web3/package.json            ./packages/web3/
COPY --from=builder /app/packages/server/package.json          ./packages/server/
COPY --from=builder /app/packages/client/package.json          ./packages/client/

# ── node_modules ──
# Bun 1.3 uses per-package node_modules (not flat hoisting). Docker COPY
# also destroys workspace symlinks. We need three things:
#   1. Root node_modules (with .bun cache containing all resolved packages)
#   2. Manual workspace symlinks (@hyperscape/* -> packages/*)
#   3. Per-package node_modules for every workspace member used at runtime
COPY --from=builder /app/node_modules ./node_modules

RUN mkdir -p node_modules/@hyperscape && \
    ln -s ../../packages/physx-js-webidl    node_modules/@hyperscape/physx-js-webidl && \
    ln -s ../../packages/decimation         node_modules/@hyperscape/decimation && \
    ln -s ../../packages/impostors          node_modules/@hyperscape/impostor && \
    ln -s ../../packages/procgen            node_modules/@hyperscape/procgen && \
    ln -s ../../packages/shared             node_modules/@hyperscape/shared && \
    ln -s ../../packages/plugin-hyperscape  node_modules/@hyperscape/plugin-hyperscape && \
    ln -s ../../packages/web3              node_modules/@hyperscape/web3 && \
    ln -s ../../packages/server            node_modules/@hyperscape/server && \
    ln -s ../../packages/client            node_modules/@hyperscape/client

COPY --from=builder /app/packages/server/node_modules            ./packages/server/node_modules
COPY --from=builder /app/packages/shared/node_modules            ./packages/shared/node_modules
COPY --from=builder /app/packages/procgen/node_modules           ./packages/procgen/node_modules
COPY --from=builder /app/packages/impostors/node_modules         ./packages/impostors/node_modules
COPY --from=builder /app/packages/plugin-hyperscape/node_modules ./packages/plugin-hyperscape/node_modules
COPY --from=builder /app/packages/web3/node_modules              ./packages/web3/node_modules
COPY --from=builder /app/packages/client/node_modules            ./packages/client/node_modules

# ── Build artifacts ──
COPY --from=builder /app/packages/physx-js-webidl/dist  ./packages/physx-js-webidl/dist
COPY --from=builder /app/packages/decimation/dist       ./packages/decimation/dist
COPY --from=builder /app/packages/impostors/dist        ./packages/impostors/dist
COPY --from=builder /app/packages/procgen/dist          ./packages/procgen/dist
COPY --from=builder /app/packages/shared/build          ./packages/shared/build
COPY --from=builder /app/packages/plugin-hyperscape/dist ./packages/plugin-hyperscape/dist
COPY --from=builder /app/packages/web3/dist             ./packages/web3/dist
COPY --from=builder /app/packages/server/dist           ./packages/server/dist
COPY --from=builder /app/packages/server/src            ./packages/server/src
COPY --from=builder /app/packages/server/world          ./packages/server/world
COPY --from=builder /app/packages/client/dist           ./packages/client/dist

RUN mkdir -p ./packages/server/public/live

EXPOSE 5555

CMD ["bun", "run", "start"]
```

**Key Features (Updated March 15, 2026):**

* **Bun 1.3.10**: Upgraded from 1.1.38 for Vite 6+ compatibility
* **Multi-Service Support**: Builds both client and server in single image
* **Workspace Symlinks**: Manually recreated after Docker COPY (COPY flattens symlinks)
* **Per-Package node\_modules**: Bun 1.3 doesn't hoist all deps - explicitly copied
* **better-sqlite3 Removal**: Stripped from manifests before install (segfaults under QEMU)
* **Manifest Embedding**: Copies cleaned manifests from builder stage
* **Client Build**: Includes `packages/client/dist` for multi-service deployments

**Docker Build Workarounds:**

1. **better-sqlite3 QEMU Crash**: Node-gyp native build segfaults under QEMU cross-compilation
   * Solution: Strip from package.json before install
   * Project uses bun:sqlite and PostgreSQL instead

2. **Workspace Symlinks Destroyed**: Docker COPY flattens symlinks to regular files
   * Solution: Manually recreate `node_modules/@hyperscape/*` symlinks in runtime stage
   * Bun workspace resolution requires these symlinks

3. **Bun 1.3 Per-Package node\_modules**: Bun 1.3 changed dependency resolution
   * Solution: Explicitly copy per-package node\_modules from builder
   * Packages like three, dotenv end up in `packages/*/node_modules/`

4. **Procgen Build Errors**: Type errors cause partial emit
   * Solution: `(bun run build || true)` continues build with partial output
   * Downstream consumers still get sufficient artifacts

### Vast.ai Keeper Image

The vast-keeper package uses a specialized image for GPU instance provisioning:

```dockerfile theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
FROM node:20-bookworm-slim

# Install Python 3.11+ and vastai SDK
RUN apt-get update && apt-get install -y \
    python3 \
    python3-pip \
    build-essential \
    git-lfs \
    unzip

# Install vastai SDK with --break-system-packages for Debian 12 (PEP 668)
RUN pip3 install --break-system-packages vastai

# Generate SSH keys for secure instance access
RUN ssh-keygen -t rsa -b 4096 -f /root/.ssh/id_rsa -N ""

# Install Bun
RUN curl -fsSL https://bun.sh/install | bash

# Copy application
COPY . /app
WORKDIR /app

# Install dependencies
RUN bun install

# Start keeper service
CMD ["bun", "run", "start"]
```

**Key Features:**

* **Python 3.11+**: Required for vastai-sdk (needs Python 3.10+)
* **--break-system-packages**: Required for pip3 on Debian 12 due to PEP 668
* **SSH key generation**: Enables secure access to provisioned instances
* **vastai CLI**: Installed as Python package (not binary)

## Resetting Containers

### Reset Database

<Warning>
  This deletes all local data (characters, inventory, progress).
</Warning>

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# Stop containers
docker stop hyperscape-postgres
docker rm hyperscape-postgres

# Remove volumes
docker volume rm hyperscape-postgres-data
docker volume rm server_postgres-data

# Verify
docker volume ls | grep hyperscape

# Restart
bun run dev
```

### Reset CDN

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
docker stop hyperscape-cdn
docker rm hyperscape-cdn
bun run cdn:up
```

## No Docker Alternative

If you can't run Docker locally:

### External PostgreSQL

Use a hosted database (e.g., [Neon](https://neon.tech)):

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# packages/server/.env
DATABASE_URL=postgresql://user:pass@host.neon.tech:5432/db
```

### External CDN

Host assets on cloud storage:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
# packages/server/.env and packages/client/.env
PUBLIC_CDN_URL=https://your-cdn.example.com
```

## Container Status

Check running containers:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
docker ps
```

Expected output when running:

```
CONTAINER ID   IMAGE           PORTS
abc123         postgres:15     0.0.0.0:5432->5432/tcp
def456         nginx:alpine    0.0.0.0:8080->80/tcp
```

## Logs

View container logs:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
docker logs hyperscape-postgres
docker logs hyperscape-cdn
```
