Rolling Deployment in Kubernetes: Zero-Downtime Releases with Health Gates
Where I use rolling deployment
Rolling deployment is my default release strategy for many stateless services when:
- old and new versions can run together;
- database/API changes are backward compatible;
- the release risk is moderate;
- I do not need explicit traffic percentages.
Kubernetes replaces pods gradually.
v1 v1 v1 v1
|
deploy v2
v
v1 v1 v1 v2
|
v
v1 v1 v2 v2
|
v
v1 v2 v2 v2
|
v
v2 v2 v2 v2Deployment configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: booking-service
spec:
replicas: 6
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0
selector:
matchLabels:
app: booking-service
template:
metadata:
labels:
app: booking-service
spec:
terminationGracePeriodSeconds: 30
containers:
- name: booking
image: registry.example/booking:2026.09.01
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
periodSeconds: 5
failureThreshold: 2
livenessProbe:
httpGet:
path: /health/live
port: 8080
periodSeconds: 10
failureThreshold: 3maxUnavailable: 0 keeps all desired capacity available while new pods start, but it requires enough cluster headroom for surge pods.
Readiness is the release gate
A pod should not receive traffic because the process exists.
The new version must complete:
configuration validation
database connectivity setup
cache/client initialization
route registration
required startup checksbefore readiness returns success.
I keep readiness lightweight. If readiness makes five remote API calls every few seconds, the health check can become its own traffic generator.
Graceful shutdown
During rollout Kubernetes sends SIGTERM.
NestJS:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
await app.listen(8080);
}Custom cleanup:
@Injectable()
export class ShutdownService
implements OnApplicationShutdown {
async onApplicationShutdown() {
await this.kafkaConsumer.stop();
await this.outboxPublisher.stop();
await this.db.destroy();
}
}The service should stop accepting new work, finish in-flight operations within a bounded period and close consumers/connections.
Backward-compatible APIs
During rolling deployment v1 and v2 coexist.
Therefore this is dangerous:
v2 starts requiring field "newRequiredField"
v1 clients/pods do not send itI use additive contracts first:
{
"bookingId": "B1001",
"newOptionalField": null
}then remove old behavior in a later release after every caller has migrated.
Database compatibility
Bad sequence:
DROP old_column
deploy new codebecause v1 pods may still read old_column.
Safe sequence:
1. add new schema
2. deploy code that understands old + new
3. migrate/backfill
4. switch reads/writes
5. verify
6. remove old schema laterThis is the expand-contract pattern.
Rollout status
kubectl rollout status \
deployment/booking-service \
--timeout=180sHistory:
kubectl rollout history \
deployment/booking-serviceRollback:
kubectl rollout undo \
deployment/booking-serviceRollback is safe only if data/schema changes are backward compatible.
Failure scenarios
New pods never become ready
Kubernetes pauses progress while old replicas remain. Alert on rollout timeout and inspect new pod logs/events.
New version is ready but functionally wrong
Readiness cannot detect every business defect. This is where smoke tests, synthetic transactions and canary/blue-green become useful for high-risk releases.
Cluster has no surge capacity
The rollout can stall. Capacity planning must include deployment surge.
Long-running request dies during termination
Use graceful shutdown, load-balancer draining and a termination grace period greater than expected in-flight work.
Production checklist
- Backward-compatible API.
- Backward-compatible database.
- Readiness probe.
- Liveness probe with different purpose.
- Graceful SIGTERM handling.
- Enough surge capacity.
- Rollout timeout.
- Smoke test after deployment.
- Error/latency monitoring by version.
- Tested rollback.
FAQ
Is rolling deployment zero downtime?
It can be when readiness, capacity and graceful shutdown are designed correctly.
Rolling or canary?
Rolling replaces instances gradually but usually does not measure explicit user percentages. Canary deliberately sends a small controlled traffic percentage to the new version.
Can I roll back after a DB migration?
Only if the schema/data remains compatible with the old application.
Related architecture guides
- [Production Deployment Patterns I Have Used](/articles/production-deployment-patterns-blue-green-canary-rolling)
- [Blue-Green Deployment](/articles/blue-green-deployment-kubernetes-nginx-production)
-
Zero-Downtime Database Changes
Architect's final take
Rolling deployment is simple and powerful when compatibility is engineered deliberately. Most failed rollouts I see are really contract, readiness or shutdown problems.
Your feedback helps prioritize deeper technical content.






