Kubernetes Deployment Stuck? 6 Real Reasons kubectl rollout status Never Finishes

Kubernetes Deployment Stuck? 6 Real Reasons kubectl rollout status Never Finishes
SHARE

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:

  1. Readiness probe failures
  2. maxUnavailable and maxSurge configuration
  3. PodDisruptionBudget (PDB) constraints
  4. ImagePullBackOff
  5. Failed startup probes
  6. 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

SymptomWhat to Check
0/1 RunningReadiness probe and application health
ImagePullBackOffImage name, tag, registry, credentials
ErrImagePullImage and registry access
CrashLoopBackOffApplication logs and previous logs
PendingCPU, memory, node constraints, scheduling
Startup probe failedStartup time and startup probe configuration
Readiness probe failedHealth endpoint, port, path and application state
Rollout stops after creating new PodmaxUnavailable, maxSurge, Pod health
Node eviction is blockedPodDisruptionBudget
Pods are healthy but rollout isn't progressingDeployment 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:

  1. Readiness probe failures — the container is running but Kubernetes doesn't consider it ready.
  2. maxUnavailable and maxSurge — the rolling update strategy controls how old and new Pods can coexist.
  3. PodDisruptionBudget constraints — these can matter when voluntary Pod disruptions or evictions are involved.
  4. ImagePullBackOff — Kubernetes cannot obtain the new container image.
  5. Startup probe failures — the application isn't successfully completing its startup process.
  6. 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.