Deployment Topology
Three Next.js apps on Vercel, one worker fleet on Fly.io, orchestrated by Temporal Cloud against Supabase Postgres.Production and staging run the same split topology — four independently deployable process
groups. This changed in #3243 (
chore(temporal): make split production config canonical), which promoted the split config to fly.prod.toml and
demoted the old monolith to fly.legacy.prod.toml.Runtime topology
The four process groups are sized independently:
These are committed defaults, not an inventory of currently started Machines.
See Worker groups below for worker counts.
Staging is identical in shape; it differs only in app name, Axiom dataset suffix,
OBSERVABILITY_PROBE_ENABLED = 1, --ha=false, and the extra test-document worker in
misc.
Fly configs
Five files. Two are canonical; two are rollback-only; one is superseded.The earlier footgun is closed. A bare
pnpm deploy now fails on purpose with
“Use a deploy:prod:<group> script; deploy:prod:legacy is rollback-only”, and the
monolith config is named legacy rather than shadowing the canonical one.Deploying
Per-group, one command each — the whole point of the split is thatimports can be
redeployed without disturbing accounting.
deploy:prod:{imports,accounting,reconciliation,misc} and the same for
deploy:staging:*. Each resolves to
fly deploy -c fly.prod.toml --process-groups <group>. Rollback to the monolith is
deploy:prod:legacy, which targets fly.legacy.prod.toml --process-groups app.
Staging additionally passes --ha=false.
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). Each Fly process group sets it via
dist/worker-group.js <group>. Unset starts the whole fleet — now only local development
and the legacy rollback path.
The defaults in
WORKER_GROUPS.md include Xentral: imports has 19 Workers, 132 Activity slots,
1,350 sticky cache slots, and 44 regular Prisma client slots. A complete split set has 63 regular
client slots in production and 64 in staging; two production Machines per group have 126. The lazy
primary-routed pools are additional. These are client concurrency ceilings, not reserved Postgres
sessions. Runtime sizing comes from packages/temporal-config/src/concurrency.ts.Release paths
The two halves of the platform ship differently. Vercel automatic Git deployments are enabled forprod and stage across
webapp, portal, and console. Other branches, including main, release/*,
rc/*, and feature/*, are blocked to avoid preview build costs.
The Ignored Build Step policy allows VERCEL_ENV=production or
VERCEL_GIT_COMMIT_REF=stage. Vercel uses exit code 1 to build and 0 to skip.
The webapp and portal scripts retain this policy in the repository; all three
projects also use the equivalent guard in their Vercel project settings.
The project-wide preview deployment disable switch must remain off so staging
can deploy; the branch allowlist and build guard block ordinary previews,
including builds from older branches with permissive vercel.json rules.
Worker deploys are not in .github/workflows/. WORKER_GROUPS.md documents
Fly branch deployments using the canonical split configurations. The per-group
pnpm scripts are for intentional manual deployments. Branch trigger settings
live in Fly, so verify their enabled state and source branch there before a release.
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.