Skip to main content

ADR 003: Railway + Vercel Split Deployment

2026-07-13 status: Superseded for staging + production by ADR-038 (prod cutover #701 closed 2026-06-15). Railway remains canonical for dev only (deliberate choice, #1233). This ADR's Decision/Consequences sections below describe the pre-cutover state and are now historical for staging/prod; see ADR-038 for the current posture and docs/ops/deploy.md for the live hosting table.

Date: 2026-04-22 Status: Accepted โ€” SUPERSEDED for staging/prod 2026-07-13 by ADR-038. Frontend-on-Vercel decision is unaffected. Dev remains on Railway.


Contextโ€‹

The platform has three services that need to be deployed and operated:

  1. api/ โ€” NestJS backend (needs persistent connections, WebSocket, DB access)
  2. agent/ โ€” Python FastAPI (GPU-optional, longer cold starts acceptable)
  3. frontend/ โ€” Next.js (SSR + static assets, benefits from CDN edge)

Options evaluated:

  • AWS ECS/EKS โ€” full control, high operational overhead, expensive for early stage
  • Render โ€” simpler than AWS, limited WebSocket support, no persistent volumes on cheap tiers
  • Fly.io โ€” good for edge, no managed Postgres/Redis, more config overhead
  • Railway โ€” managed Postgres + Redis, simple deploy from railway.toml, WebSocket support, persistent volumes
  • Vercel โ€” CDN-first, excellent for Next.js, no support for long-running Python or Node processes

Decisionโ€‹

Railway for all backend services (api, agent, Postgres, Redis) + Vercel for the Next.js frontend.

As of 2026-06-03 this is the dev + prod posture; staging has cut over to ECS Fargate (#1303, building on #1101). See the ECS clarification addendum below and ADR-028.

The split is deliberate:

  • Vercel's edge CDN gives the frontend global performance and zero-config Next.js deployment
  • Railway handles everything that needs persistent connections, a database, or WebSocket support

All backend services are in one Railway project (humanwork), deployed independently via railway up --service <name>. Railway manages:

  • Postgres 16 (with pgvector plugin) โ€” humanwork-postgres
  • Redis 7 โ€” humanwork-redis
  • API service โ€” humanwork-api
  • Agent service โ€” humanwork-agent

CI/CD is via GitHub Actions: test โ†’ build โ†’ railway up per service โ†’ vercel --prod for frontend โ†’ smoke test.


Consequencesโ€‹

Benefits:

  • Railway's managed Postgres eliminates DB operations overhead (backups, failover, pgvector extension installation via init script)
  • railway.toml per service makes deploy config explicit and version-controlled
  • Railway keeps last 5 deployments โ€” rollback is one CLI command
  • Vercel's CDN handles frontend scaling automatically; no capacity planning needed
  • One Railway project = one billing line, simple cost visibility

Trade-offs:

  • Railway is US-West region only currently. Multi-region (PRD target: Asia-Pacific edge) requires migration to AWS or Railway's planned multi-region support. Folded into the ECS Fargate cutover (#701) โ€” see addendum.
  • Railway has ~2 minute cold starts for the agent service (Python dependency install). Mitigated by keeping the service always-on (no scale-to-zero).
  • Split deployment means two sets of deploy credentials (Railway token + Vercel token) in GitHub Actions secrets.

Addendaโ€‹

2026-06-01 โ€” Env-driven DB pool sizing (#917, closes #450)โ€‹

DB pool sizing (DATABASE_POOL_MAX, DATABASE_POOL_MIN, DATABASE_POOL_IDLE_TIMEOUT_MS) is now env-driven on the API service. This was previously a hard-coded constant that did not match Railway's PgBouncer/pooler invariants and could exhaust pooled connections under multi-pod scale (see ADR-018). Defaults are tuned for Railway's transaction-pooler mode; non-Railway deploys (local docker, ephemeral CI) override via .env.

ECS clarificationโ€‹

The "AWS ECS/EKS" line in the options table above was decision-time context only โ€” Railway is canonical for backend services as of this ADR. As of 2026-06-01, that has begun to shift: Railway hosts production today; staging is being migrated to ECS Fargate (#1101 Terraform + GHA, #1303 staging boot, #1314 composite ecs-deploy action, #1327 api-deploy/agent-deploy on composite). The infra/terraform/, agent/deploy/ecs.deferred/, and docs/ops/ecs-prod-deployment-spec.md artifacts reflect the in-flight migration. A formal supersession ADR will land once prod also cuts over.

Region note: Production is currently deployed to Railway US-West. For Asian mid-market launch, latency from HK/Singapore is acceptable at MVP scale (~150-200ms RTT). AWS AP-Southeast deployment was previously tracked as BLK-037 and is now folded into the ECS Fargate cutover above.

2026-07-13 โ€” Formal supersession (ADR-038)โ€‹

Prod cutover (#701) closed 2026-06-15. ADR-038 is the formal supersession promised above โ€” staging and production are now AWS ECS Fargate + RDS Proxy, canonically. Railway is retained for dev only, a deliberate choice (#1233), not a lagging migration. See ADR-038 for the full decision record and docs/ops/deploy.md for the live per-environment hosting table.