CD8: Delivery and rollback

Lesson 3 of 5 · reading 15–25 minutes; practice and project time is on the module page

Zero-downtime deploys in practice

🎯You'll be able to

Combine readiness checks, graceful shutdown, start-before-stop and load balancer retries so a deploy under traffic fails no requests, and measure that it did.

🏠The everyday picture

A relay race: the next runner starts moving before the baton arrives, takes it while both are running, and only then does the first runner slow down. Drop any one of those habits and the baton hits the ground.

📘What it really is

Zero downtime needs every link in the chain:

  1. New copies start first (create_before_destroy, or a rolling update with surge).
  2. New copies get traffic only when ready. RouteRani's image has a HEALTHCHECK on /ready, and Terraform's wait = true waits for healthy before moving on. nginx sends requests only to copies that answer.
  3. Old copies stop gracefully. On SIGTERM, the API reports not ready, finishes requests in progress, then exits (CD1 Lesson 4).
  4. The load balancer retries. If a request reaches a copy at the moment it stops, nginx's proxy_next_upstream error timeout http_502 http_503 sends it to another copy.
  5. The load balancer notices changes quickly. nginx re-resolves app every 2 seconds (CD2 Lesson 4).
  6. It's measured. Send steady traffic through the deploy and count failures; "it looked fine" isn't evidence.

What breaks it: a health check that passes too early, an app that ignores SIGTERM, CMD npm start, a load balancer that caches addresses, long requests cut off by the shutdown timeout, or only one copy.

📝A worked example

Input: in the finished kit, with the 1.0.1 release live, start node tools/load.js http://localhost:8080/api/routes 45 5 in one terminal and node tools/pipeline.js 1.0.2 --skip-tests in another.

Expected output: the pipeline reports 1.0.2 is live, and the load tool reports failed: 0. On the 4-core test machine on 1 November 2026, a run like this sent 46,478 requests during the deploy with none failed. Project 2 test 6 runs the same check and allows up to 0.5% failures, because a busy laptop may be slower.

🌍Where it's used

📝Case scenario

A railway ticketing API showed a burst of errors at every deploy, which the team had accepted as normal for years. Measuring showed about 3% of requests failing for 20 seconds each time. Three changes fixed it: starting the app directly instead of through a shell script, a readiness check that waited for the cache to load, and load balancer retries on 502 and 503. Deploys then showed no failed requests.

🖱️Try it

Run the worked example, then repeat it with the starter's Dockerfile (CMD ["npm", "start"] and no health check) built as 1.0.9. Success: you see a difference in the failed count, or if both show 0, you can explain which other link in the chain is protecting you.

⚠️Common mistakes

🛠️For your project

Project 2 test 6 deploys under traffic and allows at most 0.5% failed requests; test 7 also sends traffic during a bad release and allows at most 1%.

✅Quick check

Name three things needed for a zero-downtime deploy.

Show the answer

Any three of: start new copies first, traffic only to ready copies, graceful shutdown, load balancer retries, quick discovery of changes, measurement.

Why does the API report "not ready" when it gets SIGTERM?

Show the answer

So the load balancer stops sending it new requests while it finishes the current ones.

← PreviousDeployment strategies
ProLearners Assist