Deploying a FastAPI app to Kubernetes with health probes
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. Kubernetes requires two endpoints, liveness and readiness. What's the difference between them? Liveness endpoint: Should return a 200 OK as long as the app is running. Readiness endpoint: Should return a 200 OK when 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"} 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 . 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. failureThreshold option will restart the pod after 3 consecutive failed checks. For readiness, the periodSeconds option sets the check interval to 5 seconds. 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 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. 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 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.
