Docker Deployment Guide
This guide covers Docker deployment for Hyperscape, including recent fixes and best practices for production deployments.Overview
Hyperscape uses a multi-stage Docker build that produces a single image containing both the game server and web client. The build process handles several platform-specific challenges related to Bun workspace management, native dependencies, and Vite compilation.Dockerfile Architecture
Build Stages
- node-build-tools: Provides real Node.js binary for Vite builds
- builder: Bun-based build stage that compiles all packages
- runtime: Node.js 22 runtime for production server
Runtime Requirements
Critical: The production server MUST run under Node.js 22+, not Bun. Reason: uWebSockets.js native bindings depend on Node’s N-API and fail under Bun’snode compatibility shim. The March 2026 performance overhaul requires uWS for 50+ concurrent players with 25+ AI agents.
Runtime Image: node:22-trixie-slim
- GLIBC Requirement: uWebSockets.js requires GLIBC ≥ 2.38
- Debian Trixie: Provides GLIBC 2.38+ (Bookworm only has 2.36)
- Slim Variant: Minimal image size while including required system libraries
Building the Image
Basic Build
Multi-Platform Build
Build Arguments
Known Issues and Workarounds
Issue 1: better-sqlite3 QEMU Segfault
Problem:better-sqlite3 node-gyp build segfaults under QEMU cross-compilation (e.g., building linux/amd64 on macOS ARM).
Workaround: Strip better-sqlite3 from package manifests before install:
bun:sqlite (dev) and PostgreSQL (production), so better-sqlite3 is not needed.
Issue 2: Bun Workspace Symlinks Flattened
Problem: DockerCOPY flattens symlinks. Bun workspaces use symlinks in node_modules/@hyperscape/* to reference local packages.
Workaround: Manually recreate workspace symlinks in runtime stage:
Issue 3: Bun Per-Package node_modules
Problem: Bun 1.3+ uses per-packagenode_modules (not flat hoisting). When Bun hoists deps without materializing directories, Docker COPY --from=builder fails with “file not found”.
Workaround: Defensively create all required directories before COPY:
Issue 4: Vite 8 Requires Node 22.12+
Problem: Bun 1.1.38 reports Node 22.6.0 when running Vite, but Vite 8 requires Node 22.12+. Workaround: Copy real Node.js binary fromnode:22-bookworm-slim for Vite build steps:
Environment Variables
Build-Time Variables
Runtime Variables
Required:packages/server/.env.example for complete list.
Running the Container
Basic Run
With Environment File
With Volume Mounts
Health Checks
The Dockerfile includes a health check that verifies the server is responding:Production Deployment
Railway
Railway deployment uses the Dockerfile automatically:docs/railway-dev-prod.md for Railway-specific setup.
Manual Deployment
-
Build Image:
-
Push to Registry:
-
Deploy:
Troubleshooting
Build Failures
Missing node_modules directories: Error:node_modules.
Fix: Update to latest Dockerfile (April 2026) which includes defensive mkdir -p commands.
Verification:
better-sqlite3 from manifests before install (already in latest Dockerfile).
Runtime Failures
uWebSockets.js binding errors: Error:- Verify runtime image is
node:22-trixie-slim(notoven/bun:*) - Verify GLIBC ≥ 2.38:
ldd --version - Check CMD uses
node, notbun
Performance Optimization
Build Cache
Use BuildKit cache mounts to speed up rebuilds:Multi-Stage Optimization
The Dockerfile uses multi-stage builds to minimize final image size:- Builder stage: Includes build tools (Python, make, g++, pkg-config)
- Runtime stage: Only includes runtime dependencies (libcairo, libpango, etc.)
- Builder stage: ~2.5GB (includes build tools)
- Runtime stage: ~450MB (production-ready)
Security Considerations
Secrets Management
Never bake secrets into the image:Non-Root User
The current Dockerfile runs as root. For production, consider adding a non-root user:Minimal Attack Surface
The runtime image usesnode:22-trixie-slim which:
- Excludes unnecessary packages
- Reduces attack surface
- Minimizes image size
See Also
Dockerfile.server- Production Dockerfiledocs/railway-dev-prod.md- Railway deployment guidepackages/server/.env.example- Environment variable reference.dockerignore- Files excluded from build context