Health checks and clean shutdowns
🎯You'll be able to
Give an image a health check, tell "alive" from "ready", and make sure the app receives the stop signal and finishes its work before exiting.
🏠The everyday picture
A shop opens its shutters only once the cash counter is set up, even though the staff arrived earlier. At closing time, it stops letting new customers in, serves the people already inside, and then locks up. It doesn't just switch the lights off mid-sale.
📘What it really is
-
Liveness asks "is the process working at all?" (RouteRani's
/health). Readiness asks "can it serve requests right now?" (/ready, which also checks the database). A copy that's alive but can't reach its database should get no traffic, but restarting it won't help. -
HEALTHCHECKin a Dockerfile tells Docker how to test the container:dockerfile HEALTHCHECK --interval=2s --timeout=2s --start-period=5s --retries=3 \ CMD wget -q -O /dev/null http://127.0.0.1:3000/ready || exit 1docker psthen shows(health: starting),(healthy)or(unhealthy). Load balancers, Terraform (wait = true) and orchestrators use this status to decide when a copy may receive traffic. -
Plain Docker does not restart an unhealthy container by itself; its restart policy reacts only when the process exits. Orchestrators such as Kubernetes act on failed checks (CD5).
-
Stopping:
docker stopsends the signalSIGTERM, waits 10 seconds by default, then sendsSIGKILL, which ends the process immediately. A well-behaved app, onSIGTERM: - reports "not ready" so the load balancer stops sending new requests;
- finishes requests already in progress;
-
closes database connections and exits.
-
Who receives the signal: the first process in a container (process ID 1) gets it. With
CMD ["node", "server.js"](exec form), that's Node.js. WithCMD npm startor the shell formCMD node server.js, the first process isnpmor/bin/sh, which may not pass the signal on, so the app is killed after 10 seconds with requests still open.
📝A worked example
Input: start your image with the database from CD2, send traffic with node tools/load.js http://localhost:8080/api/routes 20 10, and
run docker stop on one copy while traffic flows.
Expected output: the copy logs SIGTERM received: finishing requests in progress, /ready returns 503 during the shutdown, and the load tool reports 0 failed
requests, because nginx sends new requests to the other copy. With the starter's CMD ["npm", "start"] and no health check, the same test
can fail some requests. That's what Project 1 test 5 and Project 2 test 6 measure.
🌍Where it's used
- Rolling deploys: each old copy is stopped only after a new one reports ready (CD8).
- Cloud load balancers run their own health checks against a path such as
/readyand remove failing targets. - Autoscaling: scaling down stops copies; without clean shutdowns, every scale-down drops requests.
📝Case scenario
A railway enquiry API had a health check on / that returned 200 as soon as the web server started, before it had loaded its timetable
cache. After each deploy, new copies got traffic during the 20 seconds they took to load, and passengers saw "no trains found". Pointing
the check at a readiness path that waited for the cache fixed it.
🖱️Try it
Start one container from your image, then run docker inspect --format '{{json .State.Health}}' <name>. Stop the database container
and inspect again after 10 seconds. Success: the status moves from healthy to unhealthy, and the log shows the failing check.
⚠️Common mistakes
- Health check on a path that's always fine. Sign: copies that can't reach the database still get traffic. Fix: check readiness, including key dependencies.
- Restarting on readiness failures. Sign: all copies restart together when the database blips. Fix: use readiness to route traffic and liveness to restart.
- Starting through
npmor a shell. Sign:docker stopalways takes the full 10 seconds. Fix: use the exec formCMD ["node", "server.js"].
🛠️For your project
Project 1 test 3 checks for a HEALTHCHECK, and test 5 checks that every API copy reports healthy.
✅Quick check
What's the difference between liveness and readiness?
Show the answer
Liveness says the process works; readiness says it can serve requests now, including reaching what it depends on.
What does docker stop do?
Show the answer
Sends SIGTERM, waits (10 seconds by default), then sends SIGKILL.
