Skip to content

Per cloud

The choreography is the same everywhere — stage, gate, switch — and so is the config. What differs is who owns the traffic, and that changes what a rollback is. It is worth knowing before you pick a cloud to be brave on.

Container Apps Cloud Run ECS
who moves the traffic the tool the tool ECS
the sides labels, alternating tags, alternating roles in one release
the staged address <app>---<label>.<domain> from the API test_url, written down
after the switch previous stopped previous scales to zero previous terminated
cost of the idle side zero, or keep_warm zero zero, or bake_time
traffic --to <label> yes yes no — no side to name
rollback any time any time until the release finishes
strategy.env per side yes yes no

The reference implementation. The tool owns ingress.traffic, the sides are labels it writes, and everything in these pages works here.

Terraform sets revision_mode = "Multiple" and bootstraps the traffic block, then lets go of it. See What Terraform must do.

Container App Jobs carry no traffic, so they ride along: their templates are written at the switch, never before it.

This is also the only platform where keep_warm means anything.

The same model with fewer preconditions. There is no revision mode to switch on — several revisions with a split are always allowed — so Terraform only has to bootstrap the traffic block with a tag on the side that serves. The tagged URL comes back from the API rather than being assembled.

Two differences worth knowing:

The idle side costs nothing without being switched off. A revision with no traffic scales itself to zero.

keep_warm is refused here. Keeping a revision warm is scaling.min_instance_count on the template, which Terraform owns.

ECS is the other family. ECS has a blue/green engine of its own and it is better than one this tool could build: it owns the target groups, the listener rules and the shifting, and it can roll back on a CloudWatch alarm.

So the tool does not drive the rollout. It declares it and then answers the gate — a PAUSE lifecycle hook at POST_TEST_TRAFFIC_SHIFT, which is test traffic fully on the new side and production traffic still entirely on the old one. That is exactly where a smoke test belongs.

No Lambda and no appspec: the exit code of a shell command still decides.

strategy:
type: blue-green
bake_time: 10m # ECS only: the window before the old side is terminated
smoke: [ 'curl -fsS {{url "site"}}/healthz' ]
services:
site:
version: 27ec167
type: ecs
cluster: platform
test_url: https://site-test.internal.example

Three consequences, all from ECS owning the sides rather than the tool:

The staged side is reached through the test listener rule, and a rule is not an address — it may match on a host, a port or a header, and only the first two are reachable by URL at all. So it is written down on the target, and a blue-green ECS target without one is refused rather than staged with nothing to point a gate at.

Per-side environment exists so green calls green, which needs the two sides to alternate and keep their names. ECS swaps its own target groups, so the sides here are roles in one release rather than two standing environments.

traffic --to does not apply — there is no side to name. But rollback does, and it takes the other shape: for as long as the deployment has not finished, the previous version is still running and ECS can put the traffic back on it.

That covers the bake_time window after a switch, and a release whose pipeline died while paused at the smoke gate.

Once ECS has finished, CLEAN_UP has terminated the old tasks and going back is a deploy of the previous version — which the command says, with the line to run, rather than failing.

So bake_time is not just an accounting setting: it is how long rollback keeps working. Zero means the switch is the end of it.

A Lambda has no listener rule and nothing to shift, so it rides along with the service it shares an image with, and is written at the switch:

services:
purchase:
version: abc1234
cluster: platform
targets:
- type: ecs
name: purchase
test_url: https://purchase-test.internal.evolve-prd.example
- type: lambda
name: purchase-events
code: { bucket: labdigital-evolve-artifacts, key: "purchase-sha-{{.version}}.zip" }

Not implemented. Asking for blue-green on a helm target is an explicit refusal while planning, never a silent direct update.