Deployment & Infrastructure
Services at a Glance
| Service | Platform | What runs there |
|---|---|---|
| NestJS server | Railway | REST API + Socket.IO gateways |
| Next.js admin | Railway | Admin dashboard |
| PostgreSQL | Neon | Primary database (serverless) |
| File storage | Cloudflare R2 | Game assets (icons, images) |
| Auth | Clerk | User identity for all three apps |
| Mobile/web client | EAS Build → App Store / Google Play | Expo client |
Railway
Both the server and admin app are deployed on Railway. Railway uses Nixpacks to auto-detect the project type and build it. Deploys are triggered automatically on every push to the connected branch (main).
Server
apps/server/railway.json configures the Railway service:
json
{
"build": {
"buildCommand": "pnpm run build",
"watchPatterns": ["apps/server/**"]
},
"deploy": {
"startCommand": "pnpm run build && node dist/main"
}
}- Build:
pnpm run build→nest build→ compiles TypeScript todist/ - Start: runs the compiled output at
dist/main - Watch patterns: Railway only triggers a redeploy when files under
apps/server/**change — changes to the admin or wiki alone don't restart the server
Production URL: https://band-game-server-production.up.railway.app
Admin
No railway.json — Railway auto-detects Next.js via Nixpacks. Build is next build, start is next start.
Environment variables
All env vars are set in the Railway dashboard per service. See Environment Variables for the full list. Railway injects them at runtime; no .env file is used in production.
No CI/CD pipeline
There is no GitHub Actions (or equivalent) configuration. Merging to main triggers a Railway deploy directly — there is no automated test gate before deployment.
Neon (PostgreSQL)
The database is hosted on Neon — serverless Postgres. The server connects via a DATABASE_URL connection string.
Migrations
Migrations are managed with Drizzle Kit. Schema lives at apps/server/src/database/schema.ts; generated SQL lives at apps/server/drizzle/.
bash
# Generate SQL from schema changes
pnpm --filter @band-game/server db:generate
# Apply pending migrations (push schema to DB)
pnpm --filter @band-game/server db:push
# Open Drizzle Studio (local DB browser)
pnpm --filter @band-game/server db:studioMigrations are run manually — there is no automatic migration step in the Railway deploy pipeline. Run db:push against the Neon connection string before or after deploying a breaking schema change.
EAS Build (Expo client)
The mobile/web client is built with Expo Application Services (EAS). Three build profiles are configured in apps/client/eas.json:
| Profile | Distribution | Bundle ID | Purpose |
|---|---|---|---|
development | Internal (TestFlight / sideload) | com.klockworks.band-game.dev | Local dev with dev client |
preview | Store | com.klockworks.band-game.preview | Internal testing via stores |
production | Store | com.klockworks.band-game | Public release |
EAS project: c2b7510b-b4d0-4d1c-8352-144d0bff71a3 (owner: klock)
Current version: 0.0.2 (iOS build number: 10)
Building
bash
# From apps/client/
eas build --profile development --platform ios
eas build --profile preview --platform ios
eas build --profile production --platform iosiOS build numbers auto-increment on preview and production profiles (autoIncrement: true).
Environment variables in builds
EXPO_PUBLIC_* vars are inlined at build time by Metro — they are not runtime-injectable. The production profile hardcodes them in eas.json:
json
"production": {
"env": {
"EXPO_PUBLIC_GAME_SERVER_URL": "https://band-game-server-production.up.railway.app",
"EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY": "pk_test_..."
}
}Changing the server URL or Clerk key requires a new EAS build and app store submission.
Cloudflare R2
Static assets (menu button icons, store item images) are stored in Cloudflare R2 under the band-game/ prefix. The admin app manages uploads via presigned URLs — files go directly from the browser to R2 without passing through the server.
The public URL base (NEXT_PUBLIC_R2_PUBLIC_URL) is used by the client to load assets at runtime.
External Services Summary
| Service | Used by | Purpose |
|---|---|---|
| Clerk | Server, client, admin | Auth — same Clerk application across all three |
| Spotify API | Server | Band name validation (SpotifyService) |
| Expo Push | Server → client | Push notifications via Expo's push service |
| Neon | Server | PostgreSQL |
| Cloudflare R2 | Admin (write), client (read) | Asset storage |
Stale README
The root README.md describes an older architecture (gateway-api, redis-worker) and is no longer accurate. The current architecture is documented in this wiki.