FastAPI cookbook · Beginner

Add liveness and readiness endpoints to FastAPI

Tell Kubernetes or a load balancer when an instance is alive and ready to receive traffic.

Approach

/healthz returns 200 if the process is responsive (no dependency checks). /readyz checks critical dependencies (database, Redis) with short timeouts and returns 503 if any fail. Exclude both from auth, docs and noisy access logs.

Solution

import asyncio
from fastapi import APIRouter, Response
from sqlalchemy import text

health = APIRouter(include_in_schema=False)


@health.get("/healthz")
async def liveness() -> dict:
    return {"status": "ok"}


@health.get("/readyz")
async def readiness(response: Response) -> dict:
    async def check_db():
        async with engine.connect() as conn:
            await conn.execute(text("SELECT 1"))

    async def check_redis():
        await redis.ping()

    results = {}
    for name, check in {"database": check_db, "redis": check_redis}.items():
        try:
            await asyncio.wait_for(check(), timeout=1.5)
            results[name] = "ok"
        except Exception:
            results[name] = "fail"
    ready = all(v == "ok" for v in results.values())
    response.status_code = 200 if ready else 503
    return {"status": "ready" if ready else "not_ready", "checks": results}

app.include_router(health)

Pitfalls

  • Liveness that checks the database (restart storms during DB incidents).
  • Readiness without timeouts.
  • Exposing detailed error messages publicly.

Variations

  • Report ready=false during graceful shutdown so the load balancer drains traffic first.

Further reading: Kubernetes: Configure probes

Reference: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

More FastAPI recipes