Skip to content

All lessons

Area 07 // DeploymentLesson 122 / 14014 min read

Health checks and rollbacks

Health checks are small endpoints that tell the platform whether an instance is alive (liveness) and ready for traffic (readiness), and rollbacks return production to the last good version quickly when a release fails those checks or its metrics get worse.

Think of it like this

Analogy

A pilot runs a checklist before take-off (readiness: are we actually able to fly?), and the plane has warning lights during flight (liveness: is the engine still running?). If something is wrong on the runway, the plane returns to the gate: nobody takes off and hopes. Returning to the gate quickly is the rollback.

The problem it solves

New versions fail in ways tests don't catch: a missing secret, an index that didn't load, a dependency that crashes under real traffic, a prompt that triples latency. Without health checks, the platform sends users to broken instances; without fast rollbacks, a bad release stays live while someone debugs it. The goal is a short time to recovery (MTTR): problems detected in minutes and undone in minutes.

How it works: health checks

  1. Liveness (/health): returns 200 if the process can respond. Cheap, no external dependencies. If it fails repeatedly, the platform restarts the container.
  2. Readiness (/ready): returns 200 only when the instance can serve real requests: index or model loaded, required config and secrets present, essential dependencies reachable. Otherwise 503, and the load balancer sends it no traffic (load balancing).
  3. Startup checks: slow starters (model servers loading weights) get a startup probe or longer initial delay so they aren't killed while loading.
  4. Wire them up: Docker HEALTHCHECK, Kubernetes livenessProbe and readinessProbe, managed platforms' health check path (Docker, Kubernetes).
  5. Report the version in the response, so you can see what's running.
  6. Deep checks (does a real question get a sensible answer?) belong in smoke tests and synthetic monitoring, not in liveness.

How it works: rollbacks

  1. Keep previous versions deployable: immutable images tagged by commit, previous prompt and model labels (versioning).
  2. Gate deploys on readiness: start the new version, wait for /ready, then shift traffic; if it never becomes ready, remove it and keep the old one (the capstone's deploy.sh does exactly this).
  3. Watch after release: error rate, latency, cost, and quality signals compared with before (alerts).
  4. Roll back automatically when thresholds are breached (see the code), or with one command (kubectl rollout undo, redeploying the previous tag, moving a label back).
  5. Make data changes reversible: backward-compatible migrations (add columns first, remove later), versioned indexes kept until the new one is proven.
  6. Practise: rollback drills in staging (the capstone pipeline runs one on every deploy).
  7. Afterwards: a short incident review and a new test so it can't recur.
A deploy only takes traffic once ready; if it never gets ready, or its metrics break, it's rolled back.

See it in code

Install
pip install fastapi httpx
Needs
Python 3.10+. TestClient calls the app in-process; no server needed.

Liveness and readiness endpoints, a release missing its secret, and a metric-based rollback decision.

health.py
import os
from fastapi import FastAPI, Response
from fastapi.testclient import TestClient
 
INDEX = ["chunk-1", "chunk-2"]                      # pretend the vector index loaded at start-up
app = FastAPI()
 
@app.get("/health")                                 # LIVENESS: is the process alive? (restart if not)
def health():
    return {"status": "ok"}
 
@app.get("/ready")                                  # READINESS: can it serve real traffic right now?
def ready(response: Response):
    checks = {"index_loaded": len(INDEX) > 0,
              "api_key_present": bool(os.environ.get("ANTHROPIC_API_KEY"))}
    if not all(checks.values()):
        response.status_code = 503                  # load balancer stops sending traffic here
    return checks
 
client = TestClient(app)
print("health:", client.get("/health").status_code, client.get("/health").json())
r = client.get("/ready")
print("ready :", r.status_code, r.json())           # a bad deploy: secret not configured
os.environ["ANTHROPIC_API_KEY"] = "placeholder"
r = client.get("/ready")
print("ready :", r.status_code, r.json())
 
# ROLLBACK decision after a deploy: compare the new version's error rate with the old one's.
def should_roll_back(new_errors, new_total, old_rate, tolerance=0.01):
    return new_errors / new_total > old_rate + tolerance
print("roll back?", should_roll_back(new_errors=42, new_total=1_000, old_rate=0.005))
Output
health: 200 {'status': 'ok'}
ready : 503 {'index_loaded': True, 'api_key_present': False}
ready : 200 {'index_loaded': True, 'api_key_present': True}
roll back? True

The process was alive the whole time, so liveness stayed green; only readiness knew it couldn't do its job. The capstone RAG app has the same two endpoints, and its pipeline proves the rollback path on every deploy (ship the RAG app).

A real example

Example

Four minutes instead of an evening

A team's release changed an environment variable name; the new containers started fine but couldn't reach the vector database. Their old health check only returned "ok", so the platform sent all traffic to the broken version and every answer failed for 50 minutes while engineers searched logs.

They split the check into /health and /ready (which verifies the index and secrets), configured the platform to shift traffic only after readiness, and added an automatic rollback when the 5-minute error rate exceeded 2%. The next similar mistake never received traffic: the deploy failed in staging, and a different one that slipped through to production was rolled back automatically in four minutes.

The easy way

Start here

Add /health (always cheap) and /ready (checks your critical setup), point your platform's health check at /ready, keep the last few images, and write down the one command that redeploys the previous version. Try it once in staging.

When you grow: the production setup

Teams use liveness, readiness and startup probes tuned per service, deploy gates on readiness, progressive delivery with automated rollback on SLO breaches, synthetic monitoring that asks real questions, backward-compatible migrations and versioned indexes, rollback drills, and incident reviews tracking MTTR.

Trade-offs

What you gain

  • Broken instances never receive traffic.
  • Fast, low-stress recovery from bad releases.
  • Confidence to release often.

What it costs you

  • Badly designed checks cause restart loops or hide problems.
  • Automated rollback needs good metrics and thresholds.
  • Data migrations complicate rollbacks.

When not to use it

  • Never skip them for anything users depend on; the simplest version takes minutes to add.

Common mistakes

  • One endpoint for both liveness and readiness.
  • Liveness checks that call external APIs, causing restart storms during provider outages.
  • Health checks that always return "ok".
  • No previous image kept, so "rollback" means rebuilding.
  • Irreversible migrations shipped with the same release as the code.

Interview questions

Try answering out loud first, then open the model answer.

Liveness vs readiness probes: what goes in each for an LLM app?

Model answer

Liveness only checks that the process responds, with no external calls, so failures mean restart. Readiness checks whether the instance can serve: index or model loaded, config and secrets present, essential internal dependencies reachable; failure removes it from traffic without restarting. Slow-loading model servers also need a startup probe.

How do you design for fast rollbacks?

Model answer

Immutable, versioned artifacts (images, prompt and model labels, index versions) with previous ones retained; automated deploys gated on readiness; one-command or automatic rollback on metric breaches; backward-compatible migrations; feature flags for risky behaviour; and regular rollback drills.

A release passed health checks but answer quality dropped. How do you catch and fix it?

Model answer

Monitor quality signals after release (feedback rates, online evals on sampled traffic, guardrail block rates) alongside errors and latency, preferably through a canary comparison; roll back by moving prompt or model labels or redeploying the previous image; then add the failing cases to the golden set so the eval gate catches it next time.

Quiz

Pick an answer to see whether it’s right and why.

  1. 01What's the difference between liveness and readiness?
  2. 02Should a liveness check call the LLM provider?
  3. 03A new release can't find its API key. What should the readiness check do?
  4. 04What makes rollbacks fast?
  5. 05Why must database migrations be backward compatible?

0 of 5 answered

Key terms

Health check
A small endpoint (like /health or /ready) that the platform calls to decide whether an instance is alive and should get traffic.
Liveness probe
A check of whether the process is still alive; if it keeps failing, the platform restarts the container.
Readiness probe
A check of whether an instance can serve traffic right now; if it fails, the load balancer stops sending it requests.
Rollback
Returning to the previous known-good version of code, config, prompt or model after a bad release.
MTTR (mean time to recovery)
The average time from a problem starting to service being restored. Health checks and fast rollbacks shorten it.

Before and after

Progress is saved in this browser only.