Full lesson
Explore the full explanation, examples, and visuals at your own pace.
Terminal: rollout status
Your rollout is frozen at one of four updated. kubectl apply succeeded, so the YAML passed validation. But about ten minutes later, rollout status gives up with a progress deadline error. The manifest was accepted, yet the update never finished. So what's actually holding it back?
$ kubectl rollout status deployment/web
Waiting for deployment "web" rollout to finish: 1 out of 4 new replicas have been updated...
# about 10 minutes later
error: deployment "web" exceeded its progress deadlinedeployment.yaml (valid, accepted by the API)
The YAML is fine. The real problem is that v2 needs an environment variable nobody set, so /ready returns 503. Four fields matter here: maxSurge 1, maxUnavailable 0, the readiness probe, and progressDeadlineSeconds 600. Together they decide how far the rollout moves and when it gives up.
spec:
replicas: 4
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
spec:
containers:
- name: web
image: shop/web:v2
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5maxSurge 1: up to 5 pods. maxUnavailable 0: 4 stay ready.
The Deployment web drives two ReplicaSets, v1 and v2. With four replicas desired, maxSurge 1 allows five pods total. maxUnavailable 0 means all four ready pods must stay available. So the controller can't remove a v1 pod yet. It adds one v2 pod first.
The new pod starts, but never passes readiness
The controller creates the v2 pod, and the container starts fine. Then the kubelet calls GET /ready, every five seconds, and gets a 503 back. So it marks the pod NotReady, and the Service keeps it out of its endpoints. Running isn't the same as Ready. The probe just keeps retrying.
Frozen: 4 old ready, 1 new not ready
The surge budget is spent, so the controller can't add another pod. And with maxUnavailable at zero, it can't remove a v1 pod either. Nothing moves. Service keeps sending traffic to the four v1 pods, which is safe. A rolling update only advances when new pods pass readiness.
With maxSurge one and maxUnavailable zero, how many v1 pods get deleted while the v2 pod stays unready?
With maxSurge one and maxUnavailable zero, how many v1 pods get deleted while the v2 pod stays unready?
- None, all four keep running
- One, to make room for v2
- All four, replaced at once
With maxSurge one and maxUnavailable zero, how many v1 pods get deleted while the v2 pod stays unready?
None, all four keep running. Zero unavailable means four pods must always be available, and the unready v2 pod does not count. So the controller cannot remove any v1 pod, and the surge budget is already used. The rollout simply waits.
- None, all four keep running
- One, to make room for v2
- All four, replaced at once
Same broken v2, different stall shape
Now change one setting. With maxUnavailable 1, the controller can remove a v1 pod and start two v2 pods. Both new pods fail readiness, so the stall leaves three ready pods instead of four. Same broken image, but the settings decide how much capacity the bad rollout costs.
What Kubernetes reports after the deadline
The counts match our budget: four desired, one updated, five total, four available. After 600 seconds, Progressing flips to False with ProgressDeadlineExceeded. That only reports failure. Kubernetes doesn't roll back, and Available stays True, because the four v1 pods are still serving traffic.
$ kubectl describe deployment web
Replicas: 4 desired | 1 updated | 5 total | 4 available | 1 unavailable
Conditions:
Type Status Reason
Available True MinimumReplicasAvailable
Progressing False ProgressDeadlineExceededDiagnose and recover
Start with kubectl get pods. The v2 pod shows READY 0 of 1. Describe it, and the events say the readiness probe failed with a 503. Then either set the missing env var and reapply, or run kubectl rollout undo.
- kubectl get pods: v2 shows READY 0/1
- Pod events: Readiness probe failed, statuscode 503
- Fix the env var, or kubectl rollout undo
Valid YAML clears only the first gate
Our stall was the readiness gate, but it's only one of several. A pod can sit unscheduled, fail to pull its image, or crash after the container starts. Each one blocks the rollout the same way, because validation checks the YAML, never what happens at runtime.
Readiness is the gate; surge and unavailable set the budget
A rolling update only advances when new pods pass readiness. maxSurge and maxUnavailable set how much room you have while it waits. So when you tune them, set maxUnavailable to protect the capacity you can't afford to lose if the rollout stalls.




