You deploy a new version of your application to Kubernetes, run: kubectl rollout status deployment nginx
…and then you wait.
And wait.
The command never finishes.
Your Deployment may show that some Pods have been updated, but Kubernetes never reports the rollout as successfully completed.
This is a common production problem. The good news is that Kubernetes usually isn't "stuck" randomly. The Deployment controller is waiting for one or more conditions to become true before it considers the rollout complete.
In this article, we'll look at six common reasons a Kubernetes Deployment rollout can remain stuck:
- Readiness probe failures
maxUnavailableandmaxSurgeconfiguration- PodDisruptionBudget (PDB) constraints
ImagePullBackOff- Failed startup probes
- Pods crashing or otherwise failing to become healthy
We'll also walk through the commands you can use to identify the actual problem in a production environment.
What does kubectl rollout status actually do?
Consider a Deployment with three replicas:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
You update the image:
kubectl set image deployment/nginx nginx=nginx:1.28
Kubernetes creates a new ReplicaSet for the updated version and gradually replaces Pods from the old ReplicaSet.
You can monitor the rollout with: kubectl rollout status deployment nginx
If everything goes well, you might see: deployment "nginx" successfully rolled out
But if the new Pods aren't considered available, the command can continue waiting.
For example:
Waiting for deployment "nginx" rollout to finish:
1 out of 3 new replicas have been updated...
The important thing to understand is that creating a new Pod is not the same thing as successfully rolling out a new Pod.
A Pod can exist, have a container running, and still not be considered ready to serve traffic.
That's where most rollout problems begin.
First: Find out what Kubernetes is waiting for
Before changing anything, inspect the Deployment and its Pods.
Start with: kubectl rollout status deployment nginx
Then check the Deployment: kubectl get deployment nginx
For more details: kubectl describe deployment nginx
Now check the Pods: kubectl get pods
A useful command is: kubectl get pods -o wide
Then inspect a suspicious Pod: kubectl describe pod <pod-name>
Finally, check recent cluster events: kubectl get events --sort-by=.lastTimestamp
Events are particularly useful because Kubernetes often tells you exactly why a Pod isn't progressing.
You might see messages such as: Readiness probe failed
or: Failed to pull image or: Back-off restarting failed container or: Startup probe failed
Once you know the error, the problem becomes much easier to diagnose.
Reason 1: Readiness Probe Failure
One of the most common reasons a Deployment doesn't finish rolling out is a failed readiness probe.
A readiness probe tells Kubernetes:
"Is this container ready to receive traffic?"
For example:
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Kubernetes periodically sends an HTTP request to: http://<pod-ip>:8080/health
If the application responds successfully, Kubernetes can consider the Pod ready.
If the health endpoint returns an error, times out, or cannot be reached, the Pod remains unready.
You might see: kubectl get pods
with output similar to:
NAME READY STATUS RESTARTS
nginx-7d9f6c8f7d-abc12 0/1 Running 0
nginx-7d9f6c8f7d-def34 1/1 Running 0
nginx-7d9f6c8f7d-ghi56 1/1 Running 0
The important detail is: 0/1
The container is running, but it isn't ready. This distinction is extremely important.
Running does not mean Ready
A Pod can have: STATUS: Running
while still having: READY: 0/1
For example, your application might have started its process but still be:
-
Loading configuration
-
Connecting to a database
-
Running migrations
-
Loading a large model
-
Waiting for another service
-
Failing its health endpoint
-
Listening on a different port
-
Returning HTTP 500 or 503 from its health endpoint
In that situation, Kubernetes keeps the Pod out of the ready set. Because the new Pod isn't ready, the Deployment may not be able to finish replacing the old Pods.
How to diagnose it
Run: kubectl describe pod <pod-name>
Look at the Events section.
You might find:
Warning Unhealthy 30s kubelet
Readiness probe failed: HTTP probe failed with statuscode: 503
This tells you that the application is running but its readiness endpoint is returning HTTP 503.
Check the application logs: kubectl logs <pod-name>
If the Pod has multiple containers: kubectl logs <pod-name> -c <container-name>
You should also verify that the probe configuration matches the application.
For example, if the application listens on port 8080:
containers:
- name: app
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
A common mistake is configuring the probe for the wrong port:
readinessProbe:
httpGet:
path: /health
port: 80
while the application is actually listening on port 8080.
The container can be completely healthy from the application's perspective, but Kubernetes will still mark it unready because the configured health check cannot reach the application.
What to check
When you see a readiness probe failure, check:
-
Is the application actually listening on the configured port?
-
Does the health endpoint exist?
-
Does it return HTTP 200 when healthy?
-
Is the path correct?
-
Is the application taking longer to start than expected?
-
Does the application depend on another service?
-
Is authentication unexpectedly required for the health endpoint?
-
Is a network policy preventing the connection?
Fix the underlying application or probe problem rather than simply restarting the Deployment.
Reason 2: maxUnavailable and maxSurge Can Control How the Rollout Progresses
Kubernetes RollingUpdate Deployments use two important settings: maxUnavailable and: maxSurge
These determine how many Pods can be unavailable and how many additional Pods can temporarily be created during the update.
For example:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
Suppose your Deployment has: replicas: 3 With: maxUnavailable: 0
Kubernetes should maintain three available Pods during the rollout.
With: maxSurge: 1
Kubernetes can temporarily create one additional Pod above the desired replica count. So during the update, you might temporarily have four Pods.
The general idea is: Desired replicas = 3
Old Pods: 3 , New Pods:1
Total: 4
Once the new Pod becomes available, Kubernetes can remove an old Pod and continue the rollout.
What happens when the new Pod never becomes ready?
Imagine:
Desired replicas: 3
maxUnavailable: 0
maxSurge: 1
Kubernetes creates a new Pod:
Old: 3
New: 1
Total: 4
But the new Pod fails its readiness probe:
Old: 3 Ready
New: 1 NotReady
Because maxUnavailable is 0, Kubernetes cannot simply remove an existing healthy Pod to make room for the rollout. The new Pod needs to become available before the Deployment can continue making progress.
This is one reason a rollout can appear to be stuck.
Check the Deployment strategy
Run: kubectl get deployment nginx -o yaml
Look for:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
You can also inspect the Deployment: kubectl describe deployment nginx
Understanding maxUnavailable
maxUnavailable controls how many Pods can be unavailable during the update.
For example: maxUnavailable: 1
means Kubernetes can have one fewer available Pod than the desired number during the rollout.
For a three-replica Deployment, that can allow the rollout to temporarily operate with two available Pods.
A value of: maxUnavailable: 0 is more restrictive because Kubernetes should maintain the desired number of available Pods while updating.
Understanding maxSurge
maxSurge controls how many additional Pods can be created above the desired replica count.
For example:
replicas: 3
maxSurge: 1
allows Kubernetes to temporarily create four Pods.
With: maxSurge: 2
it can temporarily create five.
This can be useful when you want new Pods to become healthy before removing old Pods, but it also means the cluster needs enough resources to run the extra Pods.
If the cluster doesn't have enough CPU or memory, new Pods may remain Pending.
Check: kubectl get pods
If you see: Pending
inspect the Pod: kubectl describe pod <pod-name>
You may find: Insufficient cpu or: Insufficient memory
In that situation, the rollout isn't necessarily blocked by the application itself. The cluster may simply not have enough resources to schedule the new Pod.
Reason 3: PodDisruptionBudget Can Interact With Rollout Progress
A PodDisruptionBudget, commonly called a PDB, is designed to protect application availability during voluntary disruptions.
For example:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: nginx
This says that at least two matching Pods should remain available during voluntary disruptions.
Check your PDBs with: kubectl get pdb
For more information: kubectl describe pdb nginx-pdb
You might see information about: Allowed disruptions
and:
Current healthy
Desired healthy
An important distinction
A PodDisruptionBudget does not simply block every Deployment rollout.
PDBs primarily control voluntary disruptions such as eviction operations. A normal Deployment update has its own rollout strategy, controlled by settings such as maxUnavailable and maxSurge.
However, PDBs can become relevant when Pods are being evicted or when other cluster operations are interacting with the application's availability requirements.
For example, a node drain may attempt to evict a Pod: kubectl drain <node-name>
If the PDB doesn't allow enough disruption, the eviction can be blocked.
You may see messages indicating that eviction would violate the PodDisruptionBudget.
Therefore, when troubleshooting a rollout or broader availability problem, it's useful to inspect both: kubectl get pdb
and: kubectl get events --sort-by=.lastTimestamp
Don't assume the PDB is the cause just because one exists. Verify it through the events and current PDB status.
Reason 4: ImagePullBackOff
Another common production rollout failure occurs when Kubernetes cannot download the new container image.
For example, you update the Deployment:
kubectl set image deployment/nginx nginx=myregistry.example.com/nginx:2.0
But the image doesn't exist. Or Kubernetes doesn't have permission to access the private registry.
The new Pod may enter: ImagePullBackOff
Check: kubectl get pods
You might see:
NAME READY STATUS RESTARTS
nginx-7d9f6c8f7d-abc12 1/1 Running 0
nginx-6f8d7c5c9d-xyz99 0/1 ImagePullBackOff 0
The new Pod cannot start because Kubernetes cannot obtain its image.
Diagnose it
Run: kubectl describe pod <pod-name>
Look at Events.
You might see: Failed to pull image "myregistry.example.com/nginx:2.0"
or: pull access denied or: manifest unknown or: unauthorized
These messages are much more useful than the ImagePullBackOff status itself.
Common causes
1. Incorrect image name
For example: image: nginx:2.00
when that tag doesn't exist.
2. Incorrect image tag
Your CI/CD pipeline may have pushed: myapp:2026-09-27
while the Deployment references: myapp:2026-09-26
3. Private registry authentication
If the registry requires authentication, Kubernetes needs appropriate credentials.
For example:
imagePullSecrets:
- name: registry-secret
Check whether the secret exists: kubectl get secret
4. Registry connectivity
The node running the Pod needs to be able to communicate with the registry.
Network, DNS, firewall, or registry availability problems can prevent image downloads.
Useful commands
kubectl describe pod <pod-name>
and: kubectl get events --sort-by=.lastTimestamp
The Events section usually gives you the exact reason Kubernetes couldn't pull the image.
Reason 5: Failed Startup Probe
Startup probes are particularly important for applications that take a long time to initialize.
Consider an application that needs 60 seconds to start.
You might configure:
startupProbe:
httpGet:
path: /startup
port: 8080
failureThreshold: 30
periodSeconds: 10
Kubernetes gives the application time to successfully pass the startup probe.
If the startup probe keeps failing beyond its allowed threshold, Kubernetes considers the container startup unsuccessful and can restart the container.
You may see: Startup probe failed in the Pod events.
Check: kubectl describe pod <pod-name> and: kubectl logs <pod-name>
Why startup probes matter
A common mistake is using a probe configuration designed for a fast application on an application that requires significant startup time.
For example:
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 3
periodSeconds: 5
This allows only a relatively short startup window.
If your application needs much longer to initialize, the container may be killed before it finishes starting.
You should configure the probe according to the application's actual startup behavior rather than simply increasing the numbers without understanding the startup process.
Startup probe vs readiness probe
These two probes have different purposes.
Startup probe:
Has the application successfully started?
Readiness probe:
Is the application currently ready to receive traffic?
For a slow-starting application, you might use both:
startupProbe: httpGet: path: /startup port: 8080 failureThreshold: 30 periodSeconds: 10
readinessProbe: httpGet: path: /ready port: 8080 periodSeconds: 10
The startup probe protects the application during initialization, while the readiness probe controls whether Kubernetes should send traffic to the Pod.
Reason 6: The Container Is Crashing
Sometimes the problem isn't a probe or image at all.
The application starts and then crashes.
Check: kubectl get pods
You might see:
NAME READY STATUS RESTARTS
nginx-6f8d7c5c9d-xyz99 0/1 CrashLoopBackOff 8
CrashLoopBackOff means Kubernetes is repeatedly restarting the container and increasing the delay between restart attempts.
This can happen because of:
-
Application configuration errors
-
Missing environment variables
-
Missing configuration files
-
Invalid command-line arguments
-
Database connection failures
-
Application bugs
-
Incorrect secrets
-
Permission problems
-
Process exiting unexpectedly
Check the current logs
kubectl logs <pod-name>
If the container has already restarted, also check the previous container:
kubectl logs <pod-name> --previous
This command is extremely useful during production incidents.
For example: Error: DATABASE_URL is not configured
The Kubernetes Deployment itself may be perfectly valid.
The problem is that the application cannot start with the configuration it received.
Check the Pod events
kubectl describe pod <pod-name>
Look for: Back-off restarting failed container
The logs usually tell you why the application is exiting.
A Realistic Production Troubleshooting Flow
Imagine you deploy a new version: kubectl set image deployment/nginx nginx=nginx:1.28
Then: kubectl rollout status deployment nginx
You get:
Waiting for deployment "nginx" rollout to finish:
1 out of 3 new replicas have been updated...
Don't immediately restart the Deployment.
Start investigating.
Step 1: Check the Deployment
kubectl get deployment nginx
Then: kubectl describe deployment nginx
Look at:
-
Desired replicas
-
Updated replicas
-
Available replicas
-
Unavailable replicas
-
Deployment strategy
-
Events
Step 2: Check the Pods
kubectl get pods
Look for suspicious statuses:
Pending
ImagePullBackOff
ErrImagePull
CrashLoopBackOff
0/1
Terminating
Each status points toward a different class of problem.
Step 3: Inspect the problematic Pod
kubectl describe pod <pod-name>
Pay special attention to:
Conditions
Events
Container Statuses
Readiness
Startup probe
Image
Step 4: Check logs
kubectl logs <pod-name>
If it restarted: kubectl logs <pod-name> --previous
If there are multiple containers: kubectl logs <pod-name> -c <container-name>
Step 5: Check recent events
kubectl get events --sort-by=.lastTimestamp
Events can reveal problems that aren't obvious from: kubectl get pods
For example: Failed to pull image or: Readiness probe failed or: FailedScheduling
Quick Troubleshooting Cheat Sheet
| Symptom | What to Check |
|---|---|
0/1 Running | Readiness probe and application health |
ImagePullBackOff | Image name, tag, registry, credentials |
ErrImagePull | Image and registry access |
CrashLoopBackOff | Application logs and previous logs |
Pending | CPU, memory, node constraints, scheduling |
Startup probe failed | Startup time and startup probe configuration |
Readiness probe failed | Health endpoint, port, path and application state |
| Rollout stops after creating new Pod | maxUnavailable, maxSurge, Pod health |
| Node eviction is blocked | PodDisruptionBudget |
| Pods are healthy but rollout isn't progressing | Deployment status, ReplicaSets and events |
Useful Commands to Keep Nearby
When a Deployment is stuck, these commands cover most of the initial investigation:
Check rollout:
kubectl rollout status deployment nginx
Check Deployment:
kubectl get deployment nginx
kubectl describe deployment nginx
Check Pods
kubectl get pods
kubectl get pods -o wide
Inspect a Pod
kubectl describe pod <pod-name>
Application logs
kubectl logs <pod-name>
Logs from the previous container
kubectl logs <pod-name> --previous
Check ReplicaSets
kubectl get rs
Check PDBs
kubectl get pdb
kubectl describe pdb <pdb-name>
Check recent events
kubectl get events --sort-by=.lastTimestamp
These commands should generally be your first step before making changes to a production Deployment.
One Important Rule: Don't Blindly Restart the Deployment
When you see: kubectl rollout status deployment nginx
waiting indefinitely, restarting the Deployment may temporarily hide the symptom without fixing the cause.
For example, if the actual problem is: Readiness probe failed restarting the Deployment doesn't fix the health endpoint.
If the problem is: ImagePullBackOff restarting the Deployment doesn't make a nonexistent image appear in the registry.
If the problem is: CrashLoopBackOff restarting the Deployment doesn't fix the application configuration.
Instead, identify the condition Kubernetes is waiting for.
Final Takeaway
A Kubernetes Deployment that never finishes rolling out is usually waiting for something to become healthy or available.
The most common things to investigate are:
- Readiness probe failures — the container is running but Kubernetes doesn't consider it ready.
maxUnavailableandmaxSurge— the rolling update strategy controls how old and new Pods can coexist.- PodDisruptionBudget constraints — these can matter when voluntary Pod disruptions or evictions are involved.
ImagePullBackOff— Kubernetes cannot obtain the new container image.- Startup probe failures — the application isn't successfully completing its startup process.
- Container crashes — the application starts and then repeatedly exits.
The most useful troubleshooting sequence is:
kubectl rollout status deployment nginx
kubectl get pods
kubectl describe deployment nginx
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> --previous
kubectl get events --sort-by=.lastTimestamp
Don't ask only:
"Why is my Deployment stuck?"
Ask:
"What condition is Kubernetes waiting for before it considers this rollout complete?"
Once you identify that condition, the problem is usually much easier to isolate and fix.
