Skip to content

Deployment & Infrastructure

Services at a Glance

ServicePlatformWhat runs there
NestJS serverRailwayREST API + Socket.IO gateways
Next.js adminRailwayAdmin dashboard
PostgreSQLNeonPrimary database (serverless)
File storageCloudflare R2Game assets (icons, images)
AuthClerkUser identity for all three apps
Mobile/web clientEAS Build → App Store / Google PlayExpo 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 buildnest build → compiles TypeScript to dist/
  • 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:studio

Migrations 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:

ProfileDistributionBundle IDPurpose
developmentInternal (TestFlight / sideload)com.klockworks.band-game.devLocal dev with dev client
previewStorecom.klockworks.band-game.previewInternal testing via stores
productionStorecom.klockworks.band-gamePublic 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 ios

iOS 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

ServiceUsed byPurpose
ClerkServer, client, adminAuth — same Clerk application across all three
Spotify APIServerBand name validation (SpotifyService)
Expo PushServer → clientPush notifications via Expo's push service
NeonServerPostgreSQL
Cloudflare R2Admin (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.