Health & Readiness
Health endpoints tell orchestrators whether a Python process is alive (liveness) and whether it can accept traffic (readiness). Split them so dependency blips drain traffic without restarting pods.
Search across all documentation pages
Health endpoints tell orchestrators whether a Python process is alive (liveness) and whether it can accept traffic (readiness). Split them so dependency blips drain traffic without restarting pods.
from fastapi import FastAPI, Response, status
app = FastAPI()
@app.get("/health/live")
def live():
return {"status": "ok"}
@app.get("/health/ready")
def ready():
if not db_ping():
return Response(content='{"status":"down"}', status_code=503, media_type="application/json")
return {"status": "ok"}When to reach for this:
/health/live only)FastAPI readiness with timeout-bound DB check and startup probe friendly lazy init.
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI, Response, status
class HealthState:
def __init__(self) -> None:
self.db_ready = False
async def check_db(self) -> bool:
try:
await asyncio.wait_for(fake_db_ping(), timeout=0.5)
self.db_ready = True
return True
except Exception:
self.db_ready = False
return False
async def fake_db_ping() -> None:
await asyncio.sleep(0.01)
state = HealthState()
@asynccontextmanager
async def lifespan(app: FastAPI):
await state.check_db()
yield
app = FastAPI(lifespan=lifespan)
@app.get("/health/live")
async def live():
return {"status": "ok"}
@app.get("/health/ready")
async def ready():
ok = await state.check_db()
body = {"status": "ok" if ok else "degraded", "db": ok}
if not ok:
return Response(content=str(body), status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
return bodyWhat this demonstrates:
| Endpoint | K8s probe | Fails when |
|---|---|---|
/health/live | liveness | deadlock (rare) |
/health/ready | readiness | deps unavailable |
/health/startup | startup | slow init |
startupProbe:
httpGet:
path: /health/ready
port: 8000
failureThreshold: 30
periodSeconds: 10{"db": false} info leak. Fix: generic body externally, details in logs/metrics.| Alternative | Use When | Don't Use When |
|---|---|---|
| TCP socket probe | Non-HTTP service | FastAPI/Django HTTP apps |
| Exec probe | Custom script check | Simple HTTP sufficient |
| Mesh health | Istio/Linkerd | Plain K8s Service |
Minimum two paths for liveness vs readiness semantics - operators and K8s docs assume split.
Only if every request requires S3 - otherwise optional dependency belongs in degraded feature flag not 503.
django-health-check app or lightweight view querying default DB connection.
200 ready, 503 not ready - some LBs accept only 200 as healthy - verify platform.
Usually unauthenticated on internal network; protect admin metrics separately.
During migrate job, old pods stay ready; new pods wait until migrations complete and pool connects.
Celery workers expose separate health via inspect ping or custom HTTP sidecar.
Align with preStop and readiness failure threshold so traffic drains cleanly.
Monitor /health/live from outside; alert on regional failures independent of kubelet.
Counter readiness_check_failures_total helps debug flaky deps without reading probe logs.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026