Deployment & Environments
Where each app runs, how environments map to branches, and how secrets flow — Railway, Vercel, OpenTofu, and Infisical.
This page covers how the platform is deployed and configured across environments. For running locally, see Local development setup.
Where things run
| Workload | Platform |
|---|---|
backend (NestJS) | Railway — a GHCR Docker image deployed by CI (deploy.yml). |
| Frontends (account, admin, consumer, docs, marketing) | Vercel (Emofy team). |
landing | A separate Vercel team (not managed by the monorepo's IaC). |
The backend is built as an immutable image tagged by commit SHA (ADR-015), so a deploy is a tag change and a rollback is pointing back at a previous SHA.
Environments and branches
Environments map to git branches and to domains (ADR-019):
| Environment | Branch | Domain |
|---|---|---|
| development | development | *.dev.emofy.net |
| staging | staging | *.staging.emofy.net |
| production | main | emofy.com.tr |
The rule of thumb: emofy.com.tr is production; emofy.net is non-prod.
Infrastructure as code
Infrastructure is managed with OpenTofu (ADR-016):
- OpenTofu owns the Vercel projects and the Railway environment/project wiring.
- Secrets never live in TF state. They are owned by Infisical and synced to the deploy targets.
- Postgres and Redis are Railway-managed and provisioned out-of-band.
Secrets: Infisical SSOT
Secrets follow a single source of truth (ADR-017) with three-way ownership:
Infisical (source of truth)
├─▶ local dev : pnpm env:pull <env> → .env (then .env.local overrides)
├─▶ CI : GitHub secrets
└─▶ runtime : synced to Vercel / Railway per deploy targetThe schema side is the folder-based @emofy/env SSOT — one schema per deploy
target — so a missing or malformed variable fails validation at boot rather than
surfacing as a runtime surprise.
Destructive-target guard
Commands that can mutate remote data (migrations/seeds against staging or prod)
are gated by ADR-014: a runtime guard plus an
agent secret-handling policy. Locally, remember to move .env.local aside so
the pulled values apply unmodified when targeting remote infra.
CI/CD
GitHub Actions runs the PR checks (check-types, lint, test) and the deploy
pipeline. The backend's deploy.yml builds and pushes the Docker image to GHCR;
Railway deploys the new SHA. Frontends deploy through Vercel's git integration.
Before shipping, pnpm ship:pre runs pre-flight verification (state sync +
deferred-work checks). For the day-to-day workflow, see
docs/ai-context/WORKFLOW.md in the repo.