Skip to content

docs(decisions): add ADR-0029 zero-downtime blue-green stack deploys - #70

Merged
swimmesberger merged 1 commit into
mainfrom
wt/zero-downtime-stack-deploy-c095f7
Aug 28, 2026
Merged

docs(decisions): add ADR-0029 zero-downtime blue-green stack deploys#70
swimmesberger merged 1 commit into
mainfrom
wt/zero-downtime-stack-deploy-c095f7

Conversation

@swimmesberger

Copy link
Copy Markdown
Owner

What

Adds ADR-0029 (status Proposed) specifying zero-downtime stack deployments, plus its index entry in docs/decisions/README.md. Specification only — no code changes.

Why

Every deploy currently has a built-in 502 window: compose up -d recreates containers stop-first, and recreated containers only rejoin the ingress network after up returns. Under products/releases (ADR-0026) a rollout multiplies that blip across every tenant. This ADR records the design for closing that window.

The design in brief

  • Opt-in DeploymentStrategy (recreate | blue-green) on the product, overridable on template/stack; default recreate, existing stacks unchanged.
  • Only routed services are duplicated into a generation-scoped compose project {base}--g{n}, attached to the base project's network/volumes via external declarations. Stateful services stay shared — duplicating a database per deploy was analyzed and rejected.
  • Readiness = a compose-file healthcheck, mandatory for routed services; the deploy polls State.Health until all healthy, fails fast on unhealthy/exited, and a failed deploy leaves the old generation serving untouched.
  • The swap is the existing atomic route-table replace under a generation-scoped alias — generation-scoped because the YARP forwarder's SocketsHttpHandler pools connections by host:port with infinite lifetime, so a stable alias would keep warm sockets on the old containers.
  • Deployment contract (validated where possible): healthcheck declared in the compose file (image HEALTHCHECK is invisible to compose config), no published host ports / container_name (refused, not stripped), aliases fit a DNS label, and versions N/N−1 must coexist against the shared DB (expand-contract migrations).
  • Drain + cleanup: bounded drain (cross-stack deploy gate released first so rollouts don't serialize), then the previous generation is removed; crash leftovers are healed by an inline prefix sweep plus a periodic DeploymentSlotReaper.

Reviewer notes

  • The Consequences section enumerates the cross-cutting implementation surface (every ComposeProjectName consumer becomes slot-aware) — that scope is the main thing to sanity-check.
  • Rejected alternatives (whole-stack duplication, quiesce-first cutover, alternating blue/green names, compose up --wait, !reset port stripping) are recorded with reasoning; a pre-swap quiesce lifecycle hook is kept as future work.
  • Status is Proposed; acceptance is intended to land with the implementation PR.

Specify opt-in blue-green deployments: routed services warm up in a
generation-scoped compose project ({base}--g{n}), a mandatory compose-file
healthcheck gates readiness, traffic swaps via the existing atomic
route-table replace under a generation-scoped alias, and the old set is
drained and removed. Stateful services stay shared in the base project;
the operator-facing deployment contract requires expand-contract
migrations. Status: Proposed — spec only, no implementation.
@swimmesberger
swimmesberger merged commit d8d3553 into main Aug 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant