Kubernetes ImagePullBackOff and ErrImagePull Fixes

avatar
Tania Dugal
Founder
|
October 14, 2026

Your Pod is not starting, and kubectl get pods shows ErrImagePull or ImagePullBackOff. These statuses usually mean Kubernetes cannot pull the container image from the registry.

The Pod events usually show why the pull failed. This guide explains how to read those events and fix the most common causes. If you are new to Pods, start with What is a Kubernetes Pod?.

What ImagePullBackOff and ErrImagePull Mean?

ImagePullBackOff is a Pod status. It means the kubelet could not pull the container image and is waiting before trying again. ErrImagePull means the image pull failed. After a failed pull, the Pod can move from ErrImagePull to ImagePullBackOff while the kubelet waits and retries.

The container stays in the Waiting state because the image has not been pulled successfully, so the container cannot start. Both statuses usually have the same causes and fixes.

If the RESTARTS count is increasing, the container has started and is being restarted. That points to a different problem, such as CrashLoopBackOff, rather than an image-pull failure.

Find the Cause of ImagePullBackOff in Three Commands

Step 1: Confirm the status

kubectl get pods
NAME     READY   STATUS         RESTARTS   AGE
badtag   0/1     ErrImagePull   0          36s

‍

An image-pull problem usually shows 0 restarts because the container has not started yet.

Step 2: Read the events

The Events section at the bottom of kubectl describe usually shows the actual reason for the failed image pull. For an interactive terminal view, read our K9s guide.

kubectl describe pod badtag | grep -A 20 '^Events:'

‍

Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  37s                default-scheduler  Successfully assigned default/badtag to kubenative-control-plane
  Normal   Pulling    23s (x2 over 36s)  kubelet            spec.containers{badtag}: Pulling image "nginx:1.29.99"
  Warning  Failed     20s (x2 over 35s)  kubelet            spec.containers{badtag}: Failed to pull image "nginx:1.29.99": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/nginx:1.29.99": failed to resolve reference "docker.io/library/nginx:1.29.99": docker.io/library/nginx:1.29.99: not found
  Warning  Failed     20s (x2 over 35s)  kubelet            spec.containers{badtag}: Error: ErrImagePull
  Normal   BackOff    5s (x2 over 34s)   kubelet            spec.containers{badtag}: Back-off pulling image "nginx:1.29.99"
  Warning  Failed     5s (x2 over 34s)   kubelet            spec.containers{badtag}: Error: ImagePullBackOff

‍

The line that starts with Failed to pull image is the important one. Look at the end of that message to see what the registry reported. Here, the registry reports:

not found

‍

That tells you the image or tag does not exist.

Events can be harder to read after the Pod has been failing for a long time. You may only see Back-off pulling image in the latest events, while the Pod still keeps the last image-pull error in its status. You can check that error with:

kubectl get pod badtag \
  -o jsonpath='{.status.containerStatuses[0].state.waiting.message}' | grep -o 'failed to resolve.*'

‍

failed to resolve reference "docker.io/library/nginx:1.29.99": docker.io/library/nginx:1.29.99: not found

Step 3: Match the message to a cause.

The exact wording can vary between container runtimes and registries, but the underlying cause is usually the same.

Text in the Pod events Cause
not found Cause 1: The image name or tag does not exist
pull access denied Cause 2: The Pod does not have the required credentials
401 Unauthorized or Unable to retrieve some image pull secrets Cause 3: The image pull Secret does not work
You have reached your pull rate limit Cause 4: Docker Hub rate limit
no such host, connection refused, i/o timeout, x509 Cause 5: The node cannot reach or trust the registry
no match for platform in manifest Cause 6: The image does not support the node's CPU architecture
Status InvalidImageName or ErrImageNeverPull Two more statuses

‍

From the failed message to the fix

‍

How Long ImagePullBackOff Waits Between Attempts?

After each failed pull, the kubelet waits longer before trying again. The planned wait starts at 10 seconds and doubles after each failure, until it reaches 300 seconds. The planned waits are 10, 20, 40, 80, 160, and then 300 seconds for every later attempt.

Here are the measured gaps between pull attempts for a Pod with a tag that does not exist:

Attempt Gap since the last attempt Planned wait
2 14 s 10 s
3 32 s 20 s
4 49 s 40 s
5 96 s 80 s
6 162 s 160 s
7 315 s 300 s
8 311 s 300 s

‍

How long the kubelet really waits

Each measured gap is a little longer than the planned wait. The kubelet checks the Pod again only every 10 to 15 seconds, and the pull attempt also takes time. So treat the planned wait as the minimum wait.

The kubelet tracks the wait for each Pod and image pair, so a new Pod starts with a fresh wait. A Pod that is waiting for a missing Secret does not start immediately when you create the Secret. It starts at the next attempt. For example, a Pod that had already waited 7 minutes needed another 233 seconds. If you delete the waiting Pod, the replacement starts with a fresh wait.

Cause 1: The image name or tag does not exist

The image name has a typo, the tag was never pushed, or someone deleted the tag. The event ends with not found, which means the registry does not have that image.

You have to fix it by pointing the Deployment to a tag that exists:

kubectl set image deployment/shop nginx=nginx:1.29

‍

deployment.apps/shop image updated

‍

kubectl get pods -l app=shop

‍

NAME                   READY   STATUS        RESTARTS   AGE
shop-c87878c54-f4rp9   1/1     Running       0          6s
shop-f489f7b7d-vgc6x   0/1     Terminating   0          26s

‍

The Deployment started a new Pod, and the new Pod pulled the image successfully. In a manifest, the same fix is one line:

containers:
- name: nginx
  image: nginx:1.29

‍

If the tag exists but the pull still fails, go back to the table in Step 3.

Cause 2: No credentials for a private registry

The image is in a private registry, but the Pod has no credentials to access it. The event says pull access denied, repository does not exist or may require authorization. The message lists both possibilities, so the event alone cannot tell you which one is true. Check both the image name and the Secret.

The credentials are stored in a Secret of type kubernetes.io/dockerconfigjson. Create one with kubectl create secret docker-registry:

kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com:5000 \
  --docker-username=<username> \
  --docker-password=<password>

‍

secret/regcred created

Then add the Secret to the Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: web
    image: registry.example.com:5000/team/api:v1
  imagePullSecrets:
  - name: regcred

A running Pod cannot add imagePullSecrets, because the API server allows changes to only a few Pod fields. A Deployment solves this because changing the Pod template starts new Pods.

You can also attach the Secret to a ServiceAccount, so every new Pod that uses that ServiceAccount gets the Secret:

kubectl patch serviceaccount default -n prod -p '{"imagePullSecrets": [{"name": "regcred"}]}'

‍

serviceaccount/default patched

Cause 3: A Secret exists but does not work

The Pod uses a Secret, but the image pull still fails. You should check the following three things:

a. The Secret is in the wrong namespace: A Pod can only use Secrets from its own namespace. The Pod below runs in team-a, but the Secret exists only in default. The kubelet shows this in an event:

kubectl describe pod web -n team-a | grep -o 'Unable to retrieve.*'

‍

Unable to retrieve some image pull secrets (regcred); attempting to pull the image may not succeed.

Create the Secret in team-a using the same kubectl create secret docker-registry command and add -n team-a. The Pod started a few seconds later:

kubectl get pods -n team-a

‍

NAME   READY   STATUS    RESTARTS   AGE
web    1/1     Running   0          39s

b. The password is wrong or expired: The message ends with 401 Unauthorized:

kubectl describe pod web -n team-b | grep -o 'unexpected status.*'

‍

unexpected status from HEAD request to http://registry.example.com:5000/v2/team/api/manifests/v1: 401 Unauthorized

‍

You have to delete the Secret and create it again with the correct values. Tokens from cloud registries can expire. An Amazon ECR token is valid for 12 hours, so a Secret created by hand stops working after 12 hours. You can let the kubelet fetch fresh credentials with a credential provider plugin.

c. The registry name does not match: The Secret stores credentials for a specific registry name. That name must match the registry name in the image. Decode the Secret to compare them:

kubectl get secret regcred -n hostmismatch --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode | jq ".auths | keys"

‍

[
  "registry.example.org:5000"
]

The image comes from registry.example.com:5000, so the kubelet finds no credentials for that registry. The Pod then shows the same message as Cause 2.

Since Kubernetes 1.35, the kubelet checks credentials even for an image that is already on the node. The feature gate is KubeletEnsureSecretPulledImages.

Cause 4: The Docker Hub rate limit

Docker Hub limits image pulls. Nodes that pull without logging in and share the same public IP address also share the same allowance. Logged-in pulls count for each account. The event contains You have reached your pull rate limit, which Docker Hub returns with HTTP status 429.

Docker publishes these limits, and they can change:

Account Pulls per 6 hours
Not logged in 100 for each IPv4 address or IPv6 /64 subnet
Personal, logged in 200
Pro, Team, Business Unlimited (subject to fair use)

‍

A pull of a multi-architecture image counts once for each architecture.

You can fix it by logging in to Docker Hub. Create a Secret for Docker Hub and attach it to the ServiceAccount, as shown in Cause 2:

kubectl create secret docker-registry dockerhub \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=<username> \
  --docker-password=<access-token>

‍

secret/dockerhub created

‍

Next, run the following command to patch the ServiceAccount:

kubectl patch serviceaccount default -n <namespace> -p '{"imagePullSecrets": [{"name": "dockerhub"}]}'
serviceaccount/default patched

Cause 5: The node cannot reach or trust the registry

You have to look for one of these four messages in the Pod events:

The message ends with Meaning
lookup registry.invalid on 192.168.65.254:53: no such host DNS cannot find the name
dial tcp 172.18.0.3:5001: connect: connection refused The name resolves, but nothing is listening on that port
dial tcp 10.255.255.1:5000: i/o timeout Packets are being dropped, often by a firewall or a missing route
tls: failed to verify certificate: x509: certificate signed by unknown authority The node does not trust the registry certificate

‍

Test from the node, not from your laptop, because they may use different networks. The command kubectl debug node starts a Pod on the node, and crictl pulls the image through the container runtime:

kubectl debug node/kubenative-control-plane --image=busybox:1.37 --profile=sysadmin -- chroot /host crictl pull registry.invalid/team/api:v1

‍

Creating debugging pod node-debugger-kubenative-control-plane-hbkl4 with container debugger on node kubenative-control-plane.

‍

kubectl logs node-debugger-kubenative-control-plane-hbkl4 | grep fatal | grep -o 'lookup.*host'

‍

lookup registry.invalid on 192.168.65.254:53: no such host

‍

This check requires crictl to be installed on the node. When you finish, delete the debug Pod:

kubectl delete pod node-debugger-kubenative-control-plane-hbkl4 --now  

‍

You have to fix it based on the cause: for DNS, correct the registry name or the node's DNS settings. For a refused connection, correct the address or port, or start the registry. For a timeout, fix the firewall or route. For a certificate error, give the node the registry CA.

For containerd, use a hosts.toml file. In the containerd config.toml file, set config_path to /etc/containerd/certs.d. Then put hosts.toml in a folder named after the registry, as in the path below. The setting is under [plugins."io.containerd.cri.v1.images".registry] in containerd 2.x and under [plugins."io.containerd.grpc.v1.cri".registry] in containerd 1.x. Setting config_path for the first time needs a containerd restart.

Cause 6: The image does not support the CPU of the node

The image exists and the credentials work, but the registry has no build for the CPU architecture of the node. The event says no match for platform in manifest. This happens when you build an image for amd64 but run it on arm64, or the reverse.

Check the architecture of the node:

kubectl get nodes -o custom-columns=NAME:.metadata.name,ARCH:.status.nodeInfo.architecture

‍

NAME                       ARCH
kubenative-control-plane   arm64

‍

You can fix it by building the image for both CPU architectures, for example with Docker Buildx:

docker buildx build --platform linux/amd64,linux/arm64 -t <image> --push .

‍

With the new multi-architecture image, the Pod ran on the arm64 node:

kubectl get pod legacy-multiarch -n arch

‍

NAME               READY   STATUS    RESTARTS   AGE
legacy-multiarch   1/1     Running   0          8m56s

Two More Statuses

These two statuses can look like image-pull errors, but they involve no registry contact.

a. InvalidImageName means the kubelet cannot parse the image name. Repository names must be lowercase:

kubectl describe pod upper -n names | grep -o 'Failed to apply.*'

‍

Failed to apply default image tag "Nginx:1.29": couldn't parse image name "Nginx:1.29": invalid reference format: repository name (library/Nginx) must be lowercase

‍

b. ErrImageNeverPull means the Pod has imagePullPolicy: Never, but the image is not on the node:

kubectl describe pod never -n names | grep -o 'Container image.*Never'

‍

Container image "nginx:1.29" is not present with pull policy of Never

‍

You have to fix the image name, set imagePullPolicy to IfNotPresent, or load the image onto the node. A running Pod does not allow you to change imagePullPolicy, so create the Pod again.

A Pod with Never can start without pulling the image once another Pod has already loaded that image onto the same node.

ImagePullBackOff Troubleshooting Example

A Deployment pulls an image from a private registry. Start with no Secret:

kubectl create deployment api --image=registry.example.com:5000/team/api:v1

‍

deployment.apps/api created

‍

kubectl get pods -l app=api

‍

NAME                   READY   STATUS         RESTARTS   AGE
api-5fb9f68cf9-8pzgg   0/1     ErrImagePull   0          30s

‍

‍

The image was not pulled. Read the event:

kubectl describe pod -l app=api | grep -o 'pull access denied.*'

‍

pull access denied, repository does not exist or may require authorization: authorization failed: no basic auth credentials

‍

The words pull access denied point to Cause 2. Check whether the Pod has an image pull Secret:

kubectl get pod -l app=api -o jsonpath='{.items[0].spec.imagePullSecrets}'

‍

The command prints nothing, so the Pod has no Secret. Create the Secret and add it to the Pod template:

kubectl create secret docker-registry regcred --docker-server=registry.example.com:5000 --docker-username=<username> --docker-password=<password>

‍

secret/regcred created

‍

kubectl patch deployment api -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}}}'

‍

deployment.apps/api patched

‍

kubectl wait --for=condition=Available deployment/api --timeout=60s

‍

deployment.apps/api condition met

‍

kubectl get pods -l app=api

‍

NAME                   READY   STATUS        RESTARTS   AGE
api-5fb9f68cf9-8pzgg   0/1     Terminating   0          31s
api-d9d9c6765-m4dsh    1/1     Running       0          1s

‍

The patch changed the Pod template, so the Deployment created a new Pod. The new Pod had a fresh pull backoff and could pull the image immediately.

Four Common Mistakes

  1. Waiting after the fix: The Pod retries only when its wait ends, so start a new Pod.
  2. Guessing the cause: pull access denied can mean a wrong name or missing credentials, so check both.
  3. Reading only the events: After a long wait, check the Pod status for the last error.
  4. Trusting the cache: On Kubernetes 1.34 and earlier, a Pod without a Secret can use an image that another Pod already pulled with a Secret. So a Pod can work on one node and fail on a new node. Since 1.35, the kubelet checks the credentials again, so a Pod without the Secret gets an error.

How to Prevent ImagePullBackOff?

These practices can help to prevent image pull problems before they happen:

a. Use version tags: Kubernetes advises against using the latest tag in production because it is hard to know which version is running. For exact repeatability, pin the image digest.

b. Know the default pull policy: Kubernetes sets imagePullPolicy when it creates the Pod and does not change it later, even if you change the image. The defaults are:

Image reference Default policy
nginx (no tag) Always
nginx:latest Always
nginx:1.29 IfNotPresent
nginx@sha256:... (digest) IfNotPresent

c. Put the Secret on the ServiceAccount: This allows new workloads to get the Secret automatically.

d. Check that the image exists: You should verify the image in your pipeline before the deploy step runs.

Conclusion

ImagePullBackOff means the kubelet could not pull the image and is waiting before it tries again. Run kubectl describe pod, read the end of the "Failed to pull image" line, match it to one of the six causes, and apply that fix.

After the fix, start a new Pod instead of waiting, because the wait can grow to 300 seconds. To stop the error coming back, use version tags and put the Secret on the ServiceAccount.

FAQ

What is the difference between ErrImagePull and ImagePullBackOff?

‍ErrImagePull means the image pull failed. ImagePullBackOff means the kubelet is waiting before trying again. Both can have the same causes and fixes.

How long does ImagePullBackOff last?

‍The kubelet keeps trying until the image pull works. The wait grows up to 300 seconds between attempts.

Why does my Pod still fail after I fixed the cause?

‍The kubelet retries only when the current wait ends, and the wait can reach 5 minutes. Start a new Pod to get a fresh wait: delete the Pod or change the Deployment template.

Can I change the retry wait?

‍No. The default image pull backoff starts at 10 seconds and grows to 300 seconds. These waits are not set through the normal kubelet configuration.

How do I force an immediate retry?

‍Delete the Pod, and the Deployment creates a new Pod with a fresh wait. For a bare Pod, change the image field instead. The new image reference starts with a fresh wait, so the kubelet tries immediately.

Do I need the Secret in every namespace?

‍Yes. A Pod can only use Secrets from its own namespace.

Why does the same image work on one node and fail on another?

‍The nodes may have different CPU architectures, different network access to the registry, or different registry trust settings. Check the node and registry configuration when the same image works on one node but not another.

Does imagePullPolicy: Never avoid pull errors?

‍No. If the image is not already on the node, the Pod shows ErrImageNeverPull.

Table of Contents