Zero-Downtime Deployments on Kubernetes
Rolling updates aren't enough when you have long-lived connections. Notes from implementing blue-green deployments with connection draining.
Rolling updates work until they don’t. WebSocket connections, gRPC streams, and in-flight HTTP requests all break when pods terminate too aggressively.
The Setup
Our API gateway handles ~12K req/s with average connection duration of 45 seconds. Standard RollingUpdate with maxUnavailable: 25% caused 502 errors during every deploy.
Solution: PreStop Hook + Readiness Probe
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15 && /app/drain"]
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 5
terminationGracePeriodSeconds: 60
The drain script:
- Marks pod as not-ready (removed from service endpoints)
- Waits for active connections to finish (max 45s)
- Signals graceful shutdown
Lessons
- Always measure connection duration before choosing drain timeout
terminationGracePeriodSecondsmust exceed drain time + buffer- Monitor
kube_pod_status_readyduring deploys
deployments , kubernetes , reliability · Kubernetes , Docker , Nginx