This week, I was updating the image of a FastAPI app in our Kubernetes cluster, but I took the whole app down because the process failed due to an incompatible dependency with our server. The updated pod was unable to start, but we didn't have health checks in place, so the deployment continued to update the other replicas, taking down all app instances.
In this tutorial, I will explain how to add a health check to your FastAPI app to ensure the deployment doesn't continue updating the pods if they fail.
Adding a health endpoint to your FastAPI app
Kubernetes requires two endpoints, liveness and readiness. What's the difference between them?
-
Liveness endpoint: Should return a
200 OKas long as the app is running. -
Readiness endpoint: Should return a
200 OKwhen the app is ready to handle traffic.
If your app takes some time from boot since it's ready to start handling traffic, it makes sense to have two different endpoints. This also prevents Kubernetes from killing the updated pod while it's still booting.
For this tutorial, we will create only one endpoint at /health. This is a minimal FastAPI app:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health_check():
return {"status": "OK"}
Containerizing the app
To run this app in Kubernetes, you should create a Docker image. This is a basic image:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY main.py .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--log-level", "warning", "--host", "0.0.0.0", "--port", "8000"]
The requirements.txt file only needs fastapi and uvicorn for this example.
Then we build the Docker image, and push it to a registry your Kubernetes cluster can access, like Docker Hub or a private registry.
docker build -t fastapi-health-app:latest .
Kubernetes deployment manifest
To deploy the app in your Kubernetes cluster, you need to create a deployment manifest.
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: fastapi-app
template:
metadata:
labels:
app: fastapi-app
spec:
containers:
- name: fastapi-app
image: fastapi-health-app:latest
imagePullPolicy: Always
ports:
- containerPort: 8000
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 1
With this configuration, both liveness and rediness probes use HTTP GET requests to the path /health on port 8000.
The initialDelaySeconds option gives the app time to start before the first health check.
For liveness, the periodSeconds option sets the check interval to 10 seconds.
The failureThreshold option will restart the pod after 3 consecutive failed checks.
For readiness, the periodSeconds option sets the check interval to 5 seconds.
The failureThreshold option will mark the pod as not ready and remove it from service load balancers after one failed check.
To expose the app you need to create a service. Here's a simple service manifest:
apiVersion: v1
kind: Service
metadata:
name: fastapi-app-service
spec:
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
type: ClusterIP
Applying the configuration
Once you've created both manifests, you should deploy them to your cluster.
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Wait a few seconds for the pods to start, then you can check the status with:
kubectl get pods
NAME READY STATUS RESTARTS AGE
fastapi-app-86b64cbbd5-9qkvd 1/1 Running 0 5m
You should see the pod in a Running state with READY 1/1 once the readiness probe passes, meaning your app is ready to start receiving requests.
Verifying the probes work
To confirm the probes are working, you can check the pod details with:
kubectl describe pod [pod-name]
Look for the Liveness and Readiness sections in the output.
...
Liveness: http-get http://:8000/health delay=5s timeout=1s period=10s #success=1 #failure=3
Readiness: http-get http://:8000/health delay=5s timeout=1s period=5s #success=1 #failure=1
...
If you port forward the service you can also test the endpoint locally:
kubectl port-forward svc/fastapi-app-service 8000:8000
You can use curl to see the endpoint response:
curl localhost:8000/health
In closing
Adding health endpoints to a FastAPI app takes only a few lines of code, and configuring Kubernetes probes is fairly easy.
The key difference is tunning the liveness and readiness probe options to your app needs.
If you want to extend this setup, you could add a different endpoint for readiness probe that verifies a database connection or a cache is available before marking the app as ready.
Top comments (0)