Expand description
Blue/green deploy: the runtime-agnostic step sequence for a zero-downtime color swap.
A blue/green service runs two interchangeable slots, blue and green, on
their own ports. At any moment one is live (Caddy routes to it) and the
other is idle. A deploy readies the new version on the idle slot, proves it
healthy, swaps the Caddy upstream over with a graceful reload (no dropped
connections), then stops the old slot. Because the old slot lingers through
the swap, rollback is just the reverse upstream swap — no rebuild.
The sequence is identical regardless of how a slot is realized — that’s the whole point. A slot is “an immutable artifact + a port”, and the two runtimes only differ in how that artifact is produced:
- podman: the artifact is an image; readying the idle slot is a
Step::PullImage, and each color is its own quadlet/container. Immutable for free, so this is the baseline and covers every language. - native: the artifact is the idle color’s own working dir (a synced,
separately-built copy of the source), so a Python/C++/Node/Rust process
keeps serving from its slot while the new code builds in the other. Readying
the idle slot is a
Step::Buildin that slot’s dir.
Either way the swap below is the same five moves, which is why this lives in one runtime-agnostic builder.
Structs§
- Color
Swap - Everything
color_swap_stepsneeds, assembled by the caller from the per-runtime render. Keeping this a plain data struct (rather than threading the registry/exposure through) makes the swap logic a pure function the tests can pin without a live host. - Native
Color Unit - Inputs to
native_color_unit.workdiris the color’s isolated slot dir;portis the slot’s allocated host port;runis the service’s[service].runcommand, executed unchanged in the slot dir.
Functions§
- color_
port_ var - Env-var name carrying one color slot’s host port:
SERVICE_PORT_HTTP_BLUEfrom the baseSERVICE_PORT_HTTP. The two slots can’t share a host port (only one process binds it), so a blue/green install allocates a pair and the renders below reference the color-specific one. - color_
quadlet_ filename - Quadlet filename for one color slot:
<service>-<color>.container. systemd’s generator turns that into the<service>-<color>.serviceunit thatcolor_unitnames, so the two stay in lockstep. - color_
swap_ steps - Build the ordered step list for a zero-downtime color swap.
- color_
unit - systemd unit name (without the
.servicesuffix) for one color slot. - expand_
color_ quadlets - Expand a podman quadlet bundle for a blue/green service: replace the single
main
<service>.containerwith its two color variants (<service>-blue.container,<service>-green.container), leaving every aux quadlet (a bundled DB, a network, a volume) untouched — only the routable app container is doubled. The main container is identified by filename, the same convention the bundle renderer uses to inject ExecStartPre, so both slots inherit those hooks. - native_
color_ unit - Render one color slot’s systemd unit for a native (non-container) service.
- podman_
color_ quadlet - Rewrite a podman main-container quadlet into one color slot’s variant: rename the container and point every published host port at the color-specific env var. The image, volumes, env file, and health command are untouched — both slots are the same artifact, differing only in identity and port. Aux quadlets (a bundled DB) are never colorized; only the routable app container is.