ADR 0010: Deployment — containers, expand/migrate/contract, Kubernetes optional

Status: Accepted

Date: 2026-07-19

Context

First production release should be one well-structured deployment, not a Kubernetes prerequisite.

Decision

  • Deployable units: web, admin, control, api, worker containers + PostgreSQL, Redis, object storage, OTel collector. admin may deploy with web initially but stays a separate app boundary; control has stricter network/auth policy.
  • Migrations follow expand → backfill → contract; zero-downtime compatible; migration locks prevent concurrent runs.
  • Environment is validated at process start by @vercia/config; processes refuse to boot on invalid config.
  • Health/readiness endpoints + graceful shutdown with queue drain in the worker.
  • Kubernetes is an optional profile (infra/k8s), never required for local dev or first release.

Consequences

  • One VM + Compose can run the whole platform; horizontal scaling is adding API/worker replicas.

Single-image runtime update (2026-10-11)

The supported single image runs the API, compiled worker, and four Next surfaces under supervisor. Nginx selects web/admin/control/docs by distinct public hostnames so each application keeps its root URLs and assets. The configuration renderer validates origins and ports; /api/v1 goes to Nest while application API routes remain with their Next app. Internal server requests use a private API URL. API readiness checks the database with a timeout. Production migrations use a separate owner before the restricted runtime processes start.

The pnpm 9 monorepo dependency graph is retained in the image; unsupported recursive pruning is not used. .dockerignore excludes local environment secrets, installed dependencies, and generated artifacts from the build context.

Explicit hosted staging and shared PostgreSQL (2026-10-11)

The owner authorized a hosted merchant staging environment with email, paid

platform subscriptions and realtime calls deferred. NODE_ENV remains production.

Only the explicit staging profile permits disabled adapters; they fail with

standard precondition errors, report unavailable capabilities and never report

successful transmission, payments or room grants. Memory email and fake billing

remain forbidden in production; fake realtime media is now forbidden there too.

Commercial production configuration remains the default and requires the real

email/billing providers. Merchant-owned commerce payment accounts remain separate.

For an existing PostgreSQL server, administrators provision a dedicated database,

restricted migration owner and runtime role before release. The migration job can

validate preprovisioned credentials without creating roles or rotating passwords.

Only the dedicated database/schema is migrated; the runtime role cannot own tables,

assume another role, bypass RLS or modify the migration ledger. Hosting reuse does

not authorize changing another application's data, credentials or configuration.