Deploy each ready pull request as its own Nomad job and Postgres database
Decision
Give each ready-for-review pull request a preview environment on the production cluster. OpenTofu, in a workspace per branch, creates a Postgres database for the branch and a Nomad job running the branch's container image. Traefik routes a per-branch hostname to that job. The job gets a Redis database number from 1 to 15, hashed from the branch name; production keeps 0.
GitHub Actions deploys the preview after the branch's build goes green and destroys it when the pull request closes. Draft pull requests and documentation-only changes get no preview, and a branch that becomes one of those loses its preview. Xcode Cloud builds of a branch point the app at that branch's preview.
The deploy also seeds the preview. OpenTofu submits the seed program as a separate Nomad batch job from the same image, with the seed's opt-in flag set in that job alone.
Previews share Redis servers and seed identical fixtures, so the server prefixes every Redis key and pub/sub channel with its Postgres database name.
Why
A feature branch could be tested only locally, which misses infrastructure behaviour, or in production, which is risky. A preview runs the branch's real image on the real cluster with its own data, and TestFlight builds exercise it from a device.
The seed program wipes and rewrites the demo dataset, so it refuses to run unless explicitly enabled and can never fire against production by accident. The deploy orchestration sets that flag, so the enabler stays outside the server.
Redis database numbers alone do not separate previews. Pub/sub channels ignore the database number, and 15 buckets hashed from branch names collide once enough branches are open.
Rejected alternatives
- The server seeds itself when it detects it is a preview. That puts the seed enabler inside the server, which the refuse-by-default guard exists to prevent.
- Separating previews by Redis database number alone. Pub/sub crosses database numbers, and the hash collides.
Consequences
Branches can be tried on production infrastructure with their own seeded data, and they clean up after themselves. Previews share production's cluster, database servers and Redis, and separate from it by naming convention, so a preview is not a security boundary against production; branch code that reaches a preview is trusted as much as code that reaches production. Preview CPU competes with production on the same nodes, which is why drafts get none.