Skip to main content

Overview

Hyperscape uses Docker for local development services:
  • Asset CDN: nginx serving game assets (development only)
  • PostgreSQL: Database for player data (development only)
In production, assets are served from Cloudflare R2 and the database runs on Railway. Docker is only used for local development.

Prerequisites

  • Docker Desktop (macOS/Windows)
  • Or Docker Engine on Linux: apt install docker.io

CDN Container (Development Only)

The local CDN serves assets during development. In production, assets are served from Cloudflare R2.

Start CDN

bun run cdn:up
This starts an nginx container serving assets from packages/server/world/assets/ on port 8080. The CDN script (scripts/cdn.mjs) uses --force-recreate to refresh volume mounts when assets are updated.

Stop CDN

bun run cdn:down

Automatic Start

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

CDN Architecture

Hyperscape uses a fully CDN-based architecture with automatic fallback: Required Manifests:
  • npcs.json - NPC definitions
  • world-areas.json - World area data
  • biomes.json - Biome definitions
  • stores.json - Shop data
  • items/{weapons,tools,resources,food,misc}.json - Item categories
Production CDN Fallback: In development mode, if the local CDN (http://localhost:8080) is missing required manifests, the server automatically falls back to the production CDN (https://assets.hyperscape.club):
// packages/server/src/startup/config.ts
if (
  nodeEnv === "development" &&
  missingRequired.length > 0 &&
  isLocalhostUrl(cdnUrl)
) {
  const fallbackCdnUrl = "https://assets.hyperscape.club";
  console.warn(`[Config] Falling back to production CDN for manifests: ${fallbackCdnUrl}`);
  await fetchFrom(fallbackCdnUrl);
}
This allows developers to start the server without cloning the full assets repository.

Asset Management

# Sync assets from Git LFS
bun run assets:sync

# Deploy to Cloudflare R2 (production)
bun run assets:deploy

# Verify CDN is working
bun run cdn:verify
The ensure-assets.mjs script detects whether you have full assets (models, audio, textures) or just manifests:
function hasFullAssets(dir) {
  // Manifests-only is treated as "missing" to trigger auto-download
  const hasWorld = dirHasNonHiddenFiles(path.join(dir, "world"));
  const hasModels = dirHasNonHiddenFiles(path.join(dir, "models"));
  return hasWorld && hasModels;
}
If only manifests are present, the script clones the full assets repository and pulls Git LFS objects.

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

cd packages/server
docker-compose up postgres    # Start PostgreSQL
docker-compose down postgres  # Stop PostgreSQL

Docker Compose

The server package includes docker-compose.yml:
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

Resetting Containers

Reset Database

This deletes all local data (characters, inventory, progress).
# 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

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):
# packages/server/.env
DATABASE_URL=postgresql://user:pass@host.neon.tech:5432/db
USE_LOCAL_POSTGRES=false

External CDN

Point to the production CDN (or host your own):
# packages/server/.env and packages/client/.env
PUBLIC_CDN_URL=https://assets.hyperscape.club
Manifest fetching: The server will automatically fetch manifests from the CDN at startup. You don’t need to clone the assets repository. Asset serving: All 3D models, textures, and audio will be loaded from the CDN. The local Docker CDN is not required.

Container Status

Check running containers:
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:
docker logs hyperscape-postgres
docker logs hyperscape-cdn

# Follow logs in real-time
docker logs -f hyperscape-postgres

Production Docker Build

The repository includes Dockerfile.server for production deployments.

Multi-Stage Build

The Dockerfile uses a two-stage build for optimal image size: Stage 1: Builder
  • Installs build dependencies (Python, Cairo, etc.)
  • Runs bun install with all dependencies
  • Builds packages in order: physx-js-webidl → shared → client → server
  • Copies client build to packages/server/public/
Stage 2: Runtime
  • Minimal production image with only runtime dependencies
  • Copies built artifacts from builder stage
  • Installs production dependencies only
  • Exposes port 5555 for game server

Building Locally

Test the production Docker build:
# Build from repository root
docker build -f Dockerfile.server -t hyperscape-server .

# Run the container
docker run -p 5555:5555 \
  -e DATABASE_URL=postgresql://... \
  -e JWT_SECRET=your-secret \
  -e PUBLIC_PRIVY_APP_ID=your-app-id \
  -e PRIVY_APP_SECRET=your-secret \
  hyperscape-server

Railway Deployment

Railway can use either Nixpacks or Dockerfile: Nixpacks (default):
  • Configured in nixpacks.toml
  • Faster builds with caching
  • Automatic dependency detection
Dockerfile (alternative):
  • More control over build process
  • Consistent with local builds
  • Useful for debugging build issues
To switch to Dockerfile on Railway:
  1. Go to project settings
  2. Change “Builder” from “Nixpacks” to “Dockerfile”
  3. Set “Dockerfile Path” to Dockerfile.server
The current production deployment uses Nixpacks for faster builds. The Dockerfile is maintained for local testing and alternative deployment platforms.