Deployment Topology
Three Next.js apps on Vercel, one worker fleet on Fly.io, orchestrated by Temporal Cloud against Supabase Postgres.Production — monolithic workers
Staging — split worker groups
Same Vercel and data layer; the worker fleet is split into four right-sized processes.Fly configs
Five files. Only two are live.
All configs share:
rolling deploy strategy, HTTP health check on :8080/health every
30s, restart = always, SIGTERM with kill_timeout = 300 (worker force deadline is
4m30s, leaving 30s for connection, logger, and telemetry cleanup).
Worker groups
WORKER_GROUP selects a subset at startup
(apps/temporal-workers/src/workers/index.ts:61-74). Unset starts the whole fleet — which
is exactly what production does.
apps/temporal-workers/WORKER_GROUPS.md lists imports at 18. That count is stale — it omits
xentral. The code produces 19. This matters because the same table derives the Prisma
client-slot budget (43 for imports, 62 for a full split set). Recompute it before any cutover to
the split production model.Release paths
The two halves of the platform ship differently — the apps automatically, the workers by hand. Vercel deploys frommain, prod, stage, release/*, rc/*, and feature/*;
everything else is blocked. Identical policy across all three apps.
Nothing in .github/workflows/ references Fly. Worker deploys are manual flyctl,
which is why they are drawn as a separate path. Database migrations run via the
migrate-db.yml workflow.
Environments
Staging additionally sets
OBSERVABILITY_PROBE_ENABLED = 1; production sets it to 0.
Notes
Region isfra for workers. Vercel regions are not pinned in any vercel.json.
Secrets are Fly secrets, not config. fly.prod.toml documents
NEXT_PUBLIC_AXIOM_TOKEN, UPLOADTHING_TOKEN, and ENCRYPTION_KEY as
fly secrets set values — they are deliberately absent from the committed config.
Workers expose no public port. The :8080/health endpoint is an internal Fly Machine
check; the workers are not behind Fly Proxy.
Connection pooling is the scaling constraint. WORKER_GROUPS.md documents Supavisor
transaction-mode pools per group, with the accounting process additionally using a lazy
prismaDirect pool capped at ACCOUNTING_QUEUE_LANES * 6 + 4.