Overview
Hyperscape uses Railway for backend deployment with automated GitHub Actions integration. The server handles both API requests and serves the built frontend for unified deployment.Architecture
Services
Automated Deployment
GitHub Actions Workflow
Deployments trigger automatically on push tomain:
- Checkout code
- Trigger Railway deployment via GraphQL API
- Wait for deployment to start
- Poll deployment status (5 attempts, 30s intervals)
- Report success or failure
Required Secrets
Configure in GitHub repository settings:
Project IDs are hardcoded in the workflow:
RAILWAY_PROJECT_ID:e5f5ba11-0380-4d71-aa0b-343d89a58c0dRAILWAY_SERVICE_ID:f0b42e3b-3001-4ef1-926f-af6c3c138777RAILWAY_ENVIRONMENT_ID:194f3565-ba2f-4c98-87d2-85dfc3a5110b
Build Configuration
Nixpacks (nixpacks.toml)
Railway uses Nixpacks for builds:
- Builds shared, server, AND client in single deployment
- Copies client build to server’s
public/directory - Skips asset download (manifests fetched from CDN at runtime)
- Forces fresh Turbo builds (no cache)
Alternative: Docker Build
For Docker-based deployments, useDockerfile.server:
Environment Variables
Required
Optional
Railway-Specific
Railway automatically sets these:Manifest Fetching
The server fetches manifests from CDN at startup instead of bundling them:items.jsonnpcs.jsonresources.jsontools.jsonbiomes.jsonworld-areas.jsonstores.jsonmusic.jsonvegetation.jsonbuildings.json
- Production: Always fetches from
PUBLIC_CDN_URL - Development: Skips if local manifests exist
- Fallback: Uses existing local manifests if CDN fetch fails
packages/server/world/assets/manifests/ and served to clients via /manifests/ route with 5-minute cache headers.
CORS Configuration
The server accepts requests from multiple origins: Production Domains:https://hyperscape.clubhttps://www.hyperscape.clubhttps://hyperscape.pages.devhttp://hyperscape.pages.dev(HTTP fallback)https://hyperscape-production.up.railway.app
http://localhost:*(any localhost port)https://*.hyperscape.pages.dev(Cloudflare preview deployments)https://*.up.railway.app(Railway preview deployments)
packages/server/src/startup/http-server.ts
Frontend Serving
The server serves the built client frompublic/ directory:
Routes:
/→public/index.html(no-cache headers)/assets/*→ Client assets (if they exist inpublic/assets/)/manifests/*→ Game data manifests (5-minute cache)/assets/world/*→ World assets (1-year cache, immutable)
index.html for client-side routing.
Debugging
Debug Endpoints
Check public directory contents:Build Verification
Railway build logs show:Health Check
Manual Setup
If not using GitHub Actions:1
Create Railway project
- Go to railway.app
- Create new project from GitHub repo
- Select
HyperscapeAI/hyperscape
2
Configure service
- Builder: Nixpacks (auto-detected)
- Root Directory:
/(monorepo root) - Start Command:
cd packages/server && bun dist/index.js
3
Add PostgreSQL
- Click “New” → “Database” → “PostgreSQL”
- Railway automatically sets
DATABASE_URL
4
Set environment variables
Add in Railway dashboard:
5
Deploy
Click “Deploy” or push to main branch.
Troubleshooting
Build Fails
Check Railway logs for error messages:- Missing dependencies → Add to
nixpacks.tomlaptPkgs - Build timeout → Optimize build steps
- Out of memory → Upgrade Railway plan
Frontend Not Loading
Symptom: 503 error or “Frontend not available” Cause: Client build not copied topublic/
Solution: Verify build command includes:
Manifests Not Loading
Symptom: Game data missing or errors about missing manifests Cause: CDN fetch failed and no local manifests exist Solution:- Verify
PUBLIC_CDN_URLis set correctly - Check CDN is accessible from Railway
- Ensure manifests exist at
${PUBLIC_CDN_URL}/manifests/
Database Connection Failed
Symptom: Server crashes with database errors Cause:DATABASE_URL not set or PostgreSQL service not added
Solution:
- Add PostgreSQL service in Railway dashboard
- Verify
DATABASE_URLis set automatically - Run migrations:
bunx drizzle-kit push
Production Checklist
- Railway project created and connected to GitHub
- PostgreSQL service added
- Environment variables configured (JWT_SECRET, PRIVY_*, PUBLIC_CDN_URL)
- GitHub Actions secrets set (RAILWAY_TOKEN)
- Build succeeds and server starts
- Frontend accessible at Railway URL
- WebSocket connections working
- Manifests fetched from CDN successfully
- Database migrations applied