On this page
  1. What is ImagePullBackOff?
  2. What causes ImagePullBackOff?
  3. How do I diagnose it?
  4. How do I fix each cause?
  5. Why does this only happen in Kubernetes?
  6. Where AI coding assistants get this wrong
  7. Checklist
  8. FAQ
    1. What is ImagePullBackOff?
    2. How do I see the actual reason the image pull failed?
    3. Why do I get ImagePullBackOff only in Kubernetes but not locally?
    4. What is the difference between ErrImagePull and ImagePullBackOff?
    5. Should I use the latest tag in production?
    6. Does deleting the pod fix ImagePullBackOff?
  9. Related topics
  10. Sources
tutorial

ImagePullBackOff: What It Means and How to Fix It

ImagePullBackOff means Kubernetes cannot download your container image. Learn the causes — bad tag, auth, rate limit, network — and how to diagnose each.

Quick answer

  • ErrImagePull is the first failure; ImagePullBackOff means it has failed repeatedly and Kubernetes is waiting before retrying.
  • The container never started, so container logs are empty — the reason is in kubectl describe pod events and the node’s kubelet log.
  • The usual causes: a tag that does not exist, missing registry credentials, anonymous pull rate limits, or no network egress from the node.
  • If it works with docker pull locally but not in the cluster, it is almost always authentication.

What is ImagePullBackOff?

ImagePullBackOff is a pod status meaning the kubelet could not download the container image, failed repeatedly, and is waiting a progressively longer interval before trying again. The state appears alongside ErrImagePull, which is the single-attempt error.

The key point for diagnosis: this is a pulling failure, not a running failure. The container has never been created, so kubectl logs returns nothing useful, and the cause lives entirely in the registry interaction. That is why the diagnostic path is different from a crash-looping container, where the logs are the answer.

Where this bites vibecoders

The specific way this bites vibecoders is a manifest that only works on the machine that wrote it. An assistant produces image: myapp:latest, you build and run it locally where the image already exists, and the deployment ships without a docker push anyone asked for. The pod is scheduled fine, then sits in ImagePullBackOff forever — while the identical tag pulled successfully from your laptop minutes earlier, which is exactly why it reads as a Kubernetes bug rather than a missing push.

What causes ImagePullBackOff?

CauseError textHow it usually happens
Tag does not existmanifest unknown / not foundYou built and pushed with a different tag, or used :latest and never pushed
Registry name wrongfailed to resolve referenceTypo, or a short name Docker Hub resolves but the node does not
No credentialsunauthorized / authentication requiredPrivate image, no imagePullSecrets configured
Rate limitedtoomanyrequestsAnonymous pulls from Docker Hub exhausted
Image not pushedmanifest unknownBuilt locally, never docker pushed
No network from the nodei/o timeout / dial tcpEgress blocked, DNS broken, proxy required
Arch mismatch / platformno matching manifestImage built for a different CPU architecture
Registry unreachable or downdial tcp: lookup ...DNS, firewall, or an outage at the registry

The first two and the third account for most cases. Start there.

How do I diagnose it?

Step 1 — Read the pod events. This narrows it to a category immediately:

kubectl describe pod my-app-7d9f8b6c4-x2k9p

The Events: section reports one of these shapes:

  Normal   Pulling           2m   kubelet  Pulling image "myapp:v1.2.3"
  Warning  Failed            2m   kubelet  Failed to pull image "myapp:v1.2.3":
                                rpc error: code = NotFound desc = failed to resolve
                                reference: myapp:v1.2.3 not found
  Normal   BackOff           2m   kubelet  Back-off pulling image "myapp:v1.2.3"

failed to resolve reference ... not found points at a tag or registry name. failed to pull and unpack image points at the node. For anything more specific, continue to step 2.

Step 2 — Read the kubelet log on the node. The registry’s actual error is here, and it is the most precise evidence available:

# On the node
journalctl -u kubelet --since "10 min ago" | grep -i "imagepull\|failed to pull"

Look for manifest unknown, unauthorized, toomanyrequests, or a dial/DNS error. Each maps directly to a fix below.

Step 3 — Reproduce the pull from the node. The definitive test, because it uses the same credentials and network the kubelet has:

# Does the image exist under that exact name and tag?
docker pull myapp:v1.2.3

# Does the registry answer at all?
curl -sI https://registry-1.docker.io/v2/ | head -1

If this fails on the node but succeeds on your laptop, the difference is credentials or network — not the image.

How do I fix each cause?

The tag does not exist. Confirm what you actually pushed, then fix the manifest to match:

# What tags exist locally?
docker images

# What did you push?
docker push myapp:v1.2.3

Then set an explicit, immutable tag in the deployment rather than :latest, so the manifest and the registry cannot drift apart. imagePullPolicy also matters: with a :latest tag Kubernetes defaults to Always, while a specific tag defaults to IfNotPresent and reuses a cached copy.

Missing credentials for a private registry. Create a registry secret and reference it from the pod:

kubectl create secret docker-registry regcred \
  --docker-server=ghcr.io \
  --docker-username=YOUR_GITHUB_USER \
  --docker-password=YOUR_TOKEN
spec:
  containers:
    - name: app
      image: ghcr.io/your-org/myapp:v1.2.3
      imagePullPolicy: IfNotPresent
  imagePullSecrets:
    - name: regcred

Use a short-lived token, not a personal password, and never commit one into a manifest — that is the scenario covered in How to Scan Your Codebase for Hardcoded Secrets and How to Automate API Key Rotation.

Hit an anonymous pull rate limit. Docker Hub limits unauthenticated pulls. The fix is to authenticate — the same secret mechanism above — or to use a registry that does not rate-limit anonymous pulls for your workload. The error text is toomanyrequests.

No network from the node. Check DNS and egress from the node itself:

# Can the node resolve and reach the registry?
curl -sI https://registry-1.docker.io/v2/ | head -1
getent hosts registry-1.docker.io

A private cloud VPS with an outbound firewall or a restrictive egress policy needs a registry mirror or a proxy configured on the node, not in your application.

Architecture mismatch. If the error is no matching manifest for linux/arm64, the image was built for x86 and the node is ARM. Build for the right platform:

docker buildx build --platform linux/amd64,linux/arm64 -t myapp:v1.2.3 --push .

Verify the fix. Watch the status change:

kubectl get pod -w

ErrImagePull / ImagePullBackOff should become ContainerCreating and then Running. If it stays in backoff, delete and recreate the pod so the kubelet starts the pull cycle fresh:

kubectl delete pod my-app-7d9f8b6c4-x2k9p

Why does this only happen in Kubernetes?

Because the kubelet is a different client from your shell, with different credentials and a different network path. The usual cases:

  • Your machine is logged in, the node is not. docker pull uses your local credentials file; the kubelet has none unless you give it an imagePullSecret.
  • You built the image locally and never pushed it. The kubelet pulls from a registry, not from your laptop, and the image does not exist there.
  • Your laptop is behind a corporate proxy, the node is not, so the node cannot reach the registry.
  • Short names resolve differently. nginx and myapp are resolved against Docker Hub by default; an unresolvable short name for your private image fails on the node.

Where AI coding assistants get this wrong

  • Using :latest, so the tag cannot be verified and imagePullPolicy: Always forces a network round trip on every start.
  • Referencing an image that was built locally but never pushed, then suggesting a build step in the cluster that does not exist.
  • Omitting imagePullSecrets for a private registry, while the local machine works fine because it has cached credentials.
  • Hardcoding a registry hostname in a manifest that is only correct on the developer’s machine.
  • Not distinguishing ErrImagePull from Pending and chasing scheduling problems when the real issue is the image.
  • No platform specified when building, so an ARM node cannot run an x86 image — the arm vs x86 distinction is covered in ARM vs x86.
  • Treating a Back-off pulling line as the cause, when it is only the kubelet’s retry behaviour; the real reason is in the Failed event immediately above it.

Checklist

  • Run kubectl describe pod and read the Failed event, not the BackOff one.
  • Check the kubelet log for the registry’s actual error: journalctl -u kubelet | grep -i "failed to pull".
  • Confirm the image and tag exist in the registry, not just on your machine.
  • Push anything you built locally before deploying it.
  • Use explicit, immutable tags and set imagePullPolicy deliberately.
  • Create an imagePullSecrets entry for private registries, using a short-lived token.
  • Authenticate to Docker Hub to avoid anonymous pull rate limits.
  • Verify DNS and egress from the node, not from your laptop.
  • Build for the node’s architecture if it is ARM.
  • Quote the full registry path (ghcr.io/org/app) rather than a short name.
  • Watch kubectl get pod -w to confirm the pod reaches Running.

FAQ

What is ImagePullBackOff?

ImagePullBackOff means the kubelet tried to download the container image, failed, and is backing off before retrying. The first failure shows as ErrImagePull; after repeated failures Kubernetes reports ImagePullBackOff. It is a pulling problem, not a running problem — the container has never started.

How do I see the actual reason the image pull failed?

Use kubectl describe pod and read the events, then check the node’s kubelet logs. The pod events give a summary such as failed to resolve reference, while the kubelet log has the specific error from the registry — manifest unknown, unauthorized, or toomanyrequests.

Why do I get ImagePullBackOff only in Kubernetes but not locally?

Usually authentication. Docker on your machine is logged in, and the node is not, so the private pull works locally and fails in the cluster. The other common cause is rate limiting, which applies to anonymous pulls from public registries regardless of what your laptop can do.

What is the difference between ErrImagePull and ImagePullBackOff?

ErrImagePull is the immediate error from a single failed attempt. Once several attempts have failed, Kubernetes reports ImagePullBackOff to indicate it is retrying with increasing delay. They describe the same underlying problem at two moments in the retry cycle.

Should I use the latest tag in production?

No. A mutable tag means the image can change without your manifest changing, which removes the link between what you deployed and what is running, and it forces a registry round trip on every pod start. Use a commit hash or a release tag, and record which one you deployed.

Does deleting the pod fix ImagePullBackOff?

Deleting the pod restarts the pull cycle, which helps if the failure was transient or if you just pushed a fixed image to the same tag. It does not help if the tag does not exist or credentials are missing, because the retry will fail identically. Fix the cause first, then recreate the pod.

Sources

Share: