Zero-Downtime Rolling Updates for Stateless Kubernetes Deployments
Update a Kubernetes Deployment image with readiness probes, rolling-update limits, and rollback for zero-downtime releases.
20 Aug 2025, 13:57 UTC

Problem and takeaway
You need to ship a new container image for a stateless application without dropping requests. A Kubernetes Deployment performs a rolling update: it creates new Pods, waits for them to become ready, then terminates old ones. If you skip readiness checks or roll out too fast, users can reach failing Pods. The practical takeaway is to update the image, gate traffic with readiness probes, verify the rollout, and only then let old Pods disappear.
Prerequisites
- A running Deployment in the target cluster, with at least two replicas so one Pod can remain available while another updates.
- A readiness probe on the container. Kubernetes sends Service traffic only to Pods whose readiness probe succeeds.
- The new image pushed to a registry that cluster nodes can reach.
- kubectl access to the namespace with permission to update Deployments, typically the edit role or higher.
This guide assumes Kubernetes 1.14 or later, where kubectl rollout pause and resume are available.
Update the container image
Run from a machine with cluster access:
kubectl set image deployment/my-app my-app=registry.example.com/my-app:v2.1.0 --namespace=production
Replace my-app after deployment/ with the Deployment name, the first my-app with the container name in the Pod template, and registry.example.com/my-app:v2.1.0 with the new image reference. The command changes the Deployment spec, which causes Kubernetes to create a new ReplicaSet.
You can control rollout speed with maxSurge and maxUnavailable. The defaults are 25% each. To avoid terminating any old Pod before a replacement is ready:
kubectl patch deployment my-app --namespace=production --type=merge -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"0%"}}}}'
maxUnavailable: 0% means no existing Pod is removed before its replacement passes readiness. maxSurge: 25% allows extra Pods beyond the replica count during the transition. Aggressive settings require spare cluster CPU and memory.
Monitor the rollout
kubectl rollout status deployment/my-app --namespace=production --timeout=5m
This command blocks until the Deployment reaches its desired state or times out. While it runs, watch Pods in another terminal:
kubectl get pods -l app=my-app -o wide --namespace=production -w
New Pods should appear with the new image tag, pass their readiness probe, and only then should old Pods terminate.
Pause and inspect if something looks wrong
If new Pods are crashing or failing readiness, pause before more old Pods terminate:
kubectl rollout pause deployment/my-app --namespace=production
Inspect events and logs:
kubectl describe deployment my-app --namespace=production
kubectl logs -l app=my-app --namespace=production --previous
Fix the image or configuration, then resume:
kubectl rollout resume deployment/my-app --namespace=production
Expected checks
kubectl rollout statuscompletes without timeout.- All desired Pods show
Readyand carry the new image tag. - The Service endpoint list contains only healthy new Pods.
- An HTTP check from a test Pod returns 200 on the application health endpoint.
kubectl rollout history deployment/my-app --namespace=productionshows the new revision.
Rollback
A rolling update changes cluster state, so reversal is supported. If the new version fails validation:
kubectl rollout undo deployment/my-app --namespace=production
This reverts the Deployment template to the previous revision. Kubernetes keeps up to ten revisions by default, controlled by revisionHistoryLimit. Rollback restores the old container image, but it does not revert external changes such as ConfigMaps, Secrets, or CRDs applied separately.
Limitations
- A missing or misconfigured readiness probe lets traffic reach new Pods before they are ready.
- Zero-downtime settings such as
maxUnavailable: 0%need enough free capacity for surge Pods. - Rollback only reverts the Deployment template; dependent resources are not restored automatically.
- Large Deployments take longer because Pods update in bounded batches.
Practical verification
After the rollout, confirm the image version across Pods:
kubectl get pods -l app=my-app -o wide --namespace=production
Then test through the Service from inside the cluster:
kubectl run test-client --image=busybox --rm -i --restart=Never --namespace=production -- wget -qO- http://my-app:8080/health
Replace my-app with the Service name and 8080 with the application port. A 200 response from the new version confirms traffic is reaching healthy Pods.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.