FastAPI Health Checks
Adding Health Checks and Readiness Probes — FastAPI Backend Development.
Focus: adding health checks and readiness probes
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
- 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.
- 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.
- Configure the probes — In your deployment manifest (e.g., Kubernetes), point the liveness and readiness probes to these endpoints with appropriate paths and ports.
- 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 /healthalways returns{"status": "healthy"}with 200.GET /readyreturns 200 whenDB_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
/healthfor liveness probes in most cases. - Use a
/readyendpoint 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
- Use a small library like
python-healthcheckto add structured checks with built-in integrations. - Define the probes using gRPC instead of HTTP if your platform supports it.
- Return more detailed JSON with individual check statuses and a summary code.
Real-world use cases
- Kubernetes orchestrator uses
/healthand/readyendpoints to decide when to restart a pod or remove it from the service load balancer. - A CI/CD pipeline calls the
/readyendpoint 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
/healthand/readyas lightweight HTTP endpoints in FastAPI. - Keep health checks fast and dependency-free; put dependency checks in the readiness endpoint.
- Configure
initialDelaySecondsandperiodSecondscarefully to avoid startup issues. - Return 503 from readiness when a critical dependency is down so the platform stops routing traffic.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.