FastAPI Health Checks

Adding Health Checks and Readiness Probes — FastAPI Backend Development.

Focus: adding health checks and readiness probes

Sponsored

Your API is running in production, traffic is flowing, and then it happens: the load balancer keeps sending requests to a pod that is half-dead. It hasn't crashed, but it can't reach the database, so every request times out. Users see errors, your alerting goes off, and you're left scrambling. This is the classic problem that adding health checks and readiness probes solves. Without them, your infrastructure is flying blind, treating unhealthy instances as healthy until it's too late. In this lesson, you'll learn how to add simple, robust health and readiness endpoints to your FastAPI application, so your orchestrator (like Kubernetes) can make smart decisions about routing traffic and restarting instances.

The problem this lesson solves

In any non-trivial deployment, your application runs behind a load balancer or an orchestration platform like Docker Swarm or Kubernetes. These systems need a way to know if your app is actually able to serve requests. Health checks answer the question "Is the process alive?" while readiness probes answer "Is the process ready to receive traffic?". Without these signals, your platform will route requests to instances that are starting up, shutting down, or in a broken state. This leads to user-facing errors, cascading failures, and wasted resources. You simply cannot run a reliable service at scale without them.

Core concept / mental model

Think of your application as a person. A liveness check is like checking if they're breathing — it tells you the process is running. A readiness check is like checking if they're awake and alert — it tells you the process can actually do work. In Kubernetes, these are called liveness probes and readiness probes, and they are configured separately.

Key definitions:

  • Liveness probe: Determines if the application is alive. If it fails, the platform kills the container and restarts it.
  • Readiness probe: Determines if the application is ready to serve traffic. If it fails, the platform stops sending traffic to that instance but does not restart it.

Your FastAPI app can expose both types of checks via simple HTTP endpoints. The platform periodically calls these endpoints and acts on the response code (200 = OK, anything else = failure).

How it works step by step

  1. Create a health endpoint — A simple endpoint that returns a 200 status when the app is up. It should be lightweight and not depend on external services, so it can detect liveness even if the database is down.
  2. Create a readiness endpoint — This endpoint should check the dependencies your app needs to serve traffic, such as a database connection or cache. Return 200 only when everything is ready.
  3. Configure the probes — In your deployment manifest (e.g., Kubernetes), point the liveness and readiness probes to these endpoints with appropriate paths and ports.
  4. Monitor and adjust — Use the probe responses to inform your scaling and alerting decisions. Ensure your endpoints respond quickly (under 1 second) to avoid probe timeouts.

Hands-on walkthrough

Let's build a FastAPI app with both health and readiness endpoints.

Step 1: Basic FastAPI app

Create a new Python file, main.py, and add the following:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello World"}

Step 2: Add health and readiness endpoints

Add the following endpoints to main.py:

from fastapi import FastAPI, Response
from pydantic import BaseModel
import os

app = FastAPI()

@app.get("/health")
async def health_check():
    # Simple liveness check - always return 200 if the process is running
    return {"status": "healthy"}

@app.get("/ready")
async def readiness_check(response: Response):
    # Check dependencies, e.g., database connection
    db_ok = check_database()
    if not db_ok:
        response.status_code = 503
        return {"status": "not ready"}
    return {"status": "ready"}

def check_database() -> bool:
    # Simulate a DB check
    return os.environ.get("DB_CONNECTED", "false").lower() == "true"

Step 3: Simulate a failing dependency

To test the readiness endpoint, run the app and set DB_CONNECTED:

# Test with DB connected
DB_CONNECTED=true uvicorn main:app --port 8000
# The /ready endpoint returns 200

# Test with DB not connected
DB_CONNECTED=false uvicorn main:app --port 8001
# The /ready endpoint returns 503

Expected output:

  • GET /health always returns {"status": "healthy"} with 200.
  • GET /ready returns 200 when DB_CONNECTED=true, 503 when false.

Step 4: Kubernetes configuration example

In your Kubernetes deployment, add the following probe configuration:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
    spec:
      containers:
      - name: fastapi
        image: myregistry/fastapi-app:latest
        ports:
        - containerPort: 8000
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8000
          initialDelaySeconds: 3
          periodSeconds: 5

This setup tells Kubernetes to check /health every 10 seconds and /ready every 5 seconds, starting after a short delay.

Compare options / when to choose what

There are a few ways to implement health checks:

Approach Pros Cons Use when
Simple /health endpoint Minimal code, fast Doesn't reflect dependencies You only need liveness checks
Readiness endpoint with dependency checks Reflects real service state More complex, can mask issues if overused You have dependencies like DB, cache, or external APIs
External health check libraries (e.g., healthcheck package) Pre-built checks for common services Adds a dependency, may be overkill You have many standard dependencies and want quick setup

When to choose:

  • Use simple /health for liveness probes in most cases.
  • Use a /ready endpoint that checks critical dependencies for readiness probes.
  • Avoid making health checks themselves depend on heavy services like database queries — keep them lightweight.

Troubleshooting & edge cases

  • Probe returns 503 but pod is running: The readiness check is failing because a dependency is down. Check your dependency status and fix it. The pod will not receive traffic until it returns 200.
  • Infinite crash loop: Liveness probe is failing, so Kubernetes keeps restarting the container. Check the app's startup logs — likely the app cannot connect to a required service.
  • High latency on health endpoint: If your health check performs database queries, it may slow down. Keep health checks cheap and rely on readiness for heavier checks.
  • Timeout settings: If your endpoint takes longer than the probe timeout (default 1 second), it will fail. Ensure endpoints respond quickly, especially under load.
  • Database connection pool exhaustion: If you have many readiness checks hitting the database, they can exhaust connections. Use a cached check or a lightweight query.

What you learned & what's next

You now know how to add health checks and readiness probes to a FastAPI application, the difference between liveness and readiness, and how to configure them in a Kubernetes deployment. You can apply these skills to improve the reliability of any FastAPI service you deploy.

What's next: In the next lesson, you'll learn about monitoring and metrics — exposing application metrics to Prometheus and setting up alerting. This will give you even deeper visibility into your system's health and performance.

Practice recap

For hands-on practice, extend the /ready endpoint from this lesson to check a real service, like a Redis cache. Run the app with both services available and then stop one service to see the readiness probe start failing. Observe how Kubernetes or a load balancer would react based on the HTTP status code.

Common mistakes

  • Making the health endpoint perform heavy database queries, causing timeouts and false failures.
  • Returning a 200 status for readiness even when critical dependencies are down.
  • Forgetting to set initialDelaySeconds, so the probe starts before the app is ready and causes a restart loop.
  • Using the same endpoint for both liveness and readiness, losing the distinction between alive and ready.

Variations

  1. Use a small library like python-healthcheck to add structured checks with built-in integrations.
  2. Define the probes using gRPC instead of HTTP if your platform supports it.
  3. Return more detailed JSON with individual check statuses and a summary code.

Real-world use cases

  • Kubernetes orchestrator uses /health and /ready endpoints to decide when to restart a pod or remove it from the service load balancer.
  • A CI/CD pipeline calls the /ready endpoint after deployment to verify the new version can connect to the database before promoting it to production traffic.
  • An autoscaling group uses readiness checks to scale up only when healthy instances are available, avoiding adding broken instances.

Key takeaways

  • Liveness probes answer 'is it alive?' — readiness probes answer 'is it ready to receive traffic?'
  • Expose /health and /ready as lightweight HTTP endpoints in FastAPI.
  • Keep health checks fast and dependency-free; put dependency checks in the readiness endpoint.
  • Configure initialDelaySeconds and periodSeconds carefully to avoid startup issues.
  • Return 503 from readiness when a critical dependency is down so the platform stops routing traffic.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.