Running a mis‑configured health check can add roughly 200 ms of latency per request because the kubelet performs an extra HTTP round‑trip and processes the response before forwarding traffic. When a pod crashes repeatedly, Kubernetes restarts it every 30 seconds; each restart forces a new container image pull and initialization, which can increase CPU consumption enough to add up to $150 per month for a modest 2‑CPU service. Configure Kubernetes liveness and readiness probes in Python correctly to avoid unnecessary restarts, keep traffic flowing, and keep costs predictable.
📑 Table of Contents
- 🐍 Probe Basics — Why They Matter
- 📦 Adding Probes to Deployment — How to Configure
- 🔎 Liveness Probe
- ✅ Readiness Probe
- ⚙️ Python Integration — Using Startup and Shutdown Hooks
- 🔧 Advanced Tuning — Timeouts and Failure Thresholds
- 🟩 Final Thoughts
- ❓ Frequently Asked Questions
- What is the difference between liveness and readiness probes?
- Can I use TCP or exec probes instead of HTTP?
- How do I debug a failing probe?
- 📚 References & Further Reading
🐍 Probe Basics — Why They Matter
A liveness probe tells the kubelet whether a container is still running correctly; a readiness probe tells the service load balancer whether the pod is ready to receive traffic.
Both probes are HTTP GET requests by default, and they invoke a specific endpoint inside the container. The endpoint should return 200 when healthy and any other status when not.
In a Python web framework, the health endpoint is just another route. Below is a minimal FastAPI application exposing /healthz for liveness and /readyz for readiness.
# app.py
from fastapi import FastAPI, Response app = FastAPI() @app.get("/healthz")
def healthz(): # simple liveness check – always returns 200 unless the process is dead return Response(status_code=200) @app.get("/readyz")
def readyz(): # readiness can include checks such as DB connectivity # placeholder returns 200; replace with real checks in production return Response(status_code=200)
What this does:
- FastAPI instance: creates the ASGI application.
- /healthz: lightweight endpoint, ideal for liveness.
- /readyz: can be expanded to perform dependency checks.
Key point: the probe endpoint must be inexpensive; otherwise the kubelet adds load to the container itself.
📦 Adding Probes to Deployment — How to Configure
A Deployment manifest includes livenessProbe and readinessProbe fields that define the HTTP request, timeout, period, and failure thresholds.
🔎 Liveness Probe
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata: name: api-server
spec: replicas: 3 selector: matchLabels: app: api-server template: metadata: labels: app: api-server spec: containers: - name: api image: myrepo/api-server:latest ports: - containerPort: 80 livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 3
What this does:
-
httpGet: kubelet issues an HTTP GET to
/healthzon port 80. - initialDelaySeconds: wait 10 s before the first check, allowing the app to start.
- periodSeconds: perform the check every 5 s.
- timeoutSeconds: consider the check failed if no response within 2 s.
- failureThreshold: after 3 consecutive failures, the pod is killed and restarted.
✅ Readiness Probe
# add to the same deployment.yaml (delta)
readinessProbe: httpGet: path: /readyz port: 80 initialDelaySeconds: 5 periodSeconds: 3 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 2
What this does:
-
httpGet: checks
/readyzto confirm the container can serve traffic. - initialDelaySeconds: a shorter delay than liveness because readiness can become true quickly.
- successThreshold: one successful check marks the pod as ready.
- failureThreshold: two failures remove the pod from Service endpoints.
Why this, not the obvious alternative? Using separate endpoints isolates startup failures (readiness) from runtime crashes (liveness), preventing a pod from being removed prematurely.
Apply the manifest: (Also read: 🐍 Configure Django with AWS RDS MySQL — A Step-by-Step Guide)
$ kubectl apply -f deployment.yaml
deployment.apps/api-server created
$ kubectl get pods -l app=api-server
NAME READY STATUS RESTARTS AGE
api-server-6d4f9c7c9b-9k2lm 1/1 Running 0 30s
api-server-6d4f9c7c9b-p8x7v 1/1 Running 0 30s
api-server-6d4f9c7c9b-qz8hb 1/1 Running 0 30s
Key point: the kubelet evaluates liveness and readiness independently, so a pod can be restarted without being removed from service traffic.
Key point: aligning initialDelaySeconds with your container’s startup time prevents premature restarts.
⚙️ Python Integration — Using Startup and Shutdown Hooks
FastAPI provides event handlers that run before the first request (startup) and after the last request (shutdown). These hooks are ideal places to set flags that the readiness endpoint can query.
# app_with_hooks.py
from fastapi import FastAPI, Response app = FastAPI()
app_ready = False @app.on_event("startup")
async def on_startup(): # simulate resource initialization, e.g., DB pool global app_ready app_ready = True @app.on_event("shutdown")
async def on_shutdown(): global app_ready app_ready = False @app.get("/healthz")
def healthz(): return Response(status_code=200) @app.get("/readyz")
def readyz(): return Response(status_code=200 if app_ready else 503)
What this does:
- app_ready flag: toggles based on lifecycle events.
- /readyz: returns 503 until the startup hook completes.
- shutdown hook: ensures the pod is marked unready before termination.
Build the container so the probe ports are exposed: (More onPythonTPoint tutorials)
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -no-cache-dir -r requirements.txt
COPY app_with_hooks.py .
EXPOSE 80
CMD ["uvicorn", "app_with_hooks:app", "--host", "0.0.0.0", "--port", "80"]
What this does: (Also read: 🐍 Mastering Argo CD app of apps for Python microservices)
- EXPOSE 80: makes the port visible to Kubernetes.
- uvicorn command: runs the ASGI server on the expected port.
Re‑apply the deployment (no changes needed; the probes already target the same paths). The readiness probe now reflects the Python startup state.
Key point: using FastAPI startup hooks lets readiness reflect actual resource availability, preventing traffic from reaching an uninitialized service.
🔧 Advanced Tuning — Timeouts and Failure Thresholds
Fine‑tuning probe parameters reduces false positives caused by temporary spikes or GC pauses.
# deployment-advanced.yaml
apiVersion: apps/v1
kind: Deployment
metadata: name: api-server
spec: replicas: 2 selector: matchLabels: app: api-server template: metadata: labels: app: api-server spec: containers: - name: api image: myrepo/api-server:latest ports: - containerPort: 80 livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 15 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 5 readinessProbe: httpGet: path: /readyz port: 80 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 3
What this does:
- initialDelaySeconds (15): gives the container more time to settle before liveness checks.
- periodSeconds (10): reduces probe frequency, lowering overhead.
- failureThreshold (5): tolerates up to 5 consecutive failures, useful when occasional latency spikes occur.
According to the Kubernetes documentation, increasing failureThreshold together with a longer periodSeconds reduces the chance of premature restarts caused by transient network issues. (Also read: ⚙️ Docker Swarm vs Kubernetes for small teams — which one should you use?)
Apply the advanced manifest:
$ kubectl apply -f deployment-advanced.yaml
deployment.apps/api-server configured
Inspect the pod conditions to verify probe status:
$ kubectl describe pod -l app=api-server | grep -A2 "Readiness"
Readiness: True Message: kubelet has set the ready condition Reason: PodReady
Key point: adjusting thresholds tailors the probe behavior to your application's latency profile, preventing unnecessary restarts.
🟩 Final Thoughts
Properly Configure Kubernetes liveness and readiness probes in Python separates container health monitoring from traffic readiness, allowing the control plane to keep services stable while the application manages its own startup sequence. By exposing lightweight endpoints, tying readiness to Python lifecycle events, and tuning probe parameters, you eliminate costly restarts and keep request latency predictable.
For developers, the practical takeaway is to treat health endpoints as part of the codebase, version them alongside application logic, and treat probe configuration as an extension of your deployment pipeline rather than an afterthought.
❓ Frequently Asked Questions
What is the difference between liveness and readiness probes?
Liveness probes determine if a container should be restarted; readiness probes decide whether a pod should be added to a Service's endpoints. They operate independently, allowing a pod to be restarted without being removed from traffic.
Can I use TCP or exec probes instead of HTTP?
Yes. Kubernetes supports tcpSocket and exec probes. HTTP is preferred for web services because it allows you to return custom status codes that map directly to application health.
How do I debug a failing probe?
Run kubectl exec into the pod and curl the probe path manually. Compare the response with the probe definition, and check logs for exceptions that may cause non‑200 responses.
💡 Want to practise this hands-on? DigitalOcean gives new accounts $200 free credit for 60 days — enough to spin up a full Linux/Docker/Kubernetes environment at no cost.
📚 Recommended reading: Best DevOps & cloud books on Amazon — from Linux fundamentals to Kubernetes in production, curated for working engineers.
📚 References & Further Reading
- Kubernetes probe documentation — official description of probe fields: kubernetes.io
- FastAPI official guide — building health endpoints and event hooks: fastapi.tiangolo.com
- Docker best practices for Python images — efficient container builds: docs.docker.com
Top comments (0)