Kubernetes ImagePullBackOff and ErrImagePull: Causes, Debugging and Fixes

· 6 min read · Kubernetes Tutorials

What ImagePullBackOff means #

Before a container can start, the kubelet asks the container runtime (containerd in most clusters) to pull the image. When the pull fails, the pod shows ErrImagePull. The kubelet then retries with a growing delay (5 s, 10 s, 20 s... up to 5 minutes) and the status becomes ImagePullBackOff. The two names are the same problem at two moments: the node cannot download the image.

NAME                   READY   STATUS             RESTARTS   AGE
web-7c9d8f6b5c-kq2lw   0/1     ImagePullBackOff   0          2m

The application is not even running, so there are no logs. The answer is in the events.

Step 1: read the exact error #

kubectl describe pod <pod>

Look at the end of the Events:

Warning  Failed     Failed to pull image "myorg/web:1.2.3": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/myorg/web:1.2.3": ... not found
Warning  Failed     Error: ErrImagePull
Normal   BackOff    Back-off pulling image "myorg/web:1.2.3"
Warning  Failed     Error: ImagePullBackOff

The message after Failed to pull image decides the cause. Use this table:

Message contains Cause Fix
not found, manifest unknown, name unknown The repository or the tag does not exist Check name and tag (typos, a tag that was never pushed)
pull access denied, repository does not exist or may require authorization, unauthorized, denied, 401, 403 Private registry with no or wrong credentials, or the repository really does not exist (registries answer the same for both) Create an imagePullSecret
toomanyrequests, 429 Docker Hub rate limit for anonymous pulls Authenticate, use a mirror or ECR pull-through cache
no such host, i/o timeout, dial tcp ... connection refused, TLS handshake timeout The node cannot reach the registry: DNS, firewall, proxy, NAT, security group Fix node networking
x509: certificate signed by unknown authority A private registry with a self-signed or internal CA Add the CA to the nodes' runtime config
http: server gave HTTP response to HTTPS client An insecure (HTTP) registry Configure it as an insecure registry in the runtime
no matching manifest for linux/arm64/v8 in the manifest list entries The image has no build for the node CPU architecture Build a multi-platform image
failed to resolve reference Malformed image reference Fix the image string
ErrImageNeverPull imagePullPolicy: Never and the image is not on the node Load the image onto the node or change the policy

Common causes and fixes #

1. Typo or missing tag #

The most frequent one. Check each part of registry/repository:tag:

kubectl get pod <pod> -o jsonpath='{.spec.containers[*].image}{"\n"}'
docker pull myorg/web:1.2.3          # from your machine, to compare
docker manifest inspect myorg/web:1.2.3

Common slips: ngnix for nginx, an uppercase letter (repository names must be lowercase), latest not existing in that repository, a CI job that failed to push the tag, :v1.2.3 versus :1.2.3. Fix it in the Deployment: kubectl set image deployment/web web=myorg/web:1.2.3. If the tag was just pushed from CI, check that the pipeline finished.

2. Private registry: imagePullSecrets #

Create a Secret of type docker-registry in the same namespace as the pod and reference it:

kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=ci-bot \
  --docker-password='<token>' \
  --docker-email=ci@example.com \
  -n dev
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: web
          image: registry.example.com/team/web:1.2.3

To avoid repeating it in every pod, attach it to the ServiceAccount:

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

Checks: the Secret is in the right namespace, the server name in the Secret matches the host in the image exactly (index.docker.io versus docker.io, with or without port), and the token has read permission (read:packages on GitHub Container Registry, a personal access token for Docker Hub). Decode what you stored:

kubectl get secret regcred -n dev -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq

3. Amazon ECR #

ECR passwords expire after 12 hours, so a static docker-registry Secret stops working. On EKS give the node IAM role (or the node group role) the AmazonEC2ContainerRegistryReadOnly policy and no Secret is needed. For other clusters use a refresher (a CronJob that recreates the Secret, or the ECR credential provider of the kubelet). The repository must be in a region the node can reach, and the image URI is <account>.dkr.ecr.<region>.amazonaws.com/<repo>:<tag>. Pushing the image: push images to ECR. Errors like no basic auth credentials mean the node has no valid token.

4. Docker Hub rate limits #

toomanyrequests: You have reached your pull rate limit. Anonymous pulls are limited per IP, and all the nodes behind one NAT share it. Add Docker Hub credentials as an imagePullSecret, mirror the images to your registry or ECR (a pull-through cache), or use a different registry for base images.

5. Local images (kind, K3s, Rancher Desktop, minikube) #

An image built on your machine is not in the cluster nodes. With imagePullPolicy: Always (the default for :latest) the node tries to pull it from a registry and fails.

kind load docker-image web:dev --name lab               # kind
minikube image load web:dev                              # minikube
sudo k3s ctr images import web.tar                       # K3s (docker save web:dev -o web.tar)
nerdctl --namespace k8s.io build -t web:dev .            # Rancher Desktop with containerd

Then use a specific tag (not latest) and imagePullPolicy: IfNotPresent or Never. See kind and Rancher Desktop Kubernetes.

6. Node networking and DNS #

The error is dial tcp: lookup registry.example.com: no such host or i/o timeout. Test from the node, not from your laptop:

kubectl get pod <pod> -o wide                      # which node
kubectl debug node/<node> -it --image=ubuntu:24.04
# inside: apt-get update && apt-get install -y curl; curl -I https://registry.example.com/v2/
# or on the node over SSH:
crictl pull registry.example.com/team/web:1.2.3

Private subnets need a NAT gateway or VPC endpoints (for ECR: ecr.api, ecr.dkr and the S3 gateway endpoint). Corporate proxies need HTTP_PROXY and NO_PROXY in the runtime service configuration. Check security groups, network ACLs and firewall rules to the registry.

7. Architecture mismatch #

no matching manifest for linux/arm64/v8. Build for both architectures: docker buildx build --platform linux/amd64,linux/arm64 -t myorg/web:1.2.3 --push .. See BuildKit and buildx.

8. Wrong imagePullPolicy #

Value Behavior
IfNotPresent Pull only if the image is not on the node. The default when the tag is not latest
Always Check the registry on every start. The default for latest or no tag
Never Never pull. Fails with ErrImageNeverPull if the image is not on the node

Pin tags or digests (image: myorg/web@sha256:...). A mutable tag with IfNotPresent can run different code on different nodes.

Quick debugging checklist #

kubectl describe pod <pod> | tail -20
kubectl get pod <pod> -o jsonpath='{.spec.containers[*].image} {.spec.imagePullSecrets}{"\n"}'
kubectl get secret -n <ns>                                         # is the pull secret there?
kubectl get events -n <ns> --field-selector reason=Failed --sort-by=.lastTimestamp
docker login registry.example.com && docker pull registry.example.com/team/web:1.2.3   # outside the cluster
kubectl run pull-test --image=registry.example.com/team/web:1.2.3 --restart=Never --overrides='{"spec":{"imagePullSecrets":[{"name":"regcred"}]}}'

Scan your images and fix vulnerabilities before pushing them, see image security scanning. After fixing the cause, the kubelet retries on its own at the next back-off; to speed it up delete the pod: kubectl delete pod <pod>, or kubectl rollout restart deployment/web.

Frequently asked questions #

What is the difference between ErrImagePull and ImagePullBackOff? ErrImagePull is the failed pull. ImagePullBackOff is the waiting period before the next try. Same cause.

Why does it say "repository does not exist or may require authorization"? Registries do not reveal whether a private repository exists, so a missing repository and missing credentials give the same message.

Do imagePullSecrets work across namespaces? No. Create the Secret in every namespace that needs it, or attach it to each ServiceAccount.

Why does the image work on my machine? Your machine has it cached or is logged in. The node is a different machine with different credentials, network and CPU architecture.

How do I use an image from my laptop in the cluster? Push it to a registry, or load it into the nodes: kind load docker-image, minikube image load, k3s ctr images import, or build it with the cluster runtime in Rancher Desktop.

Does the pull happen again at every pod restart? Only if imagePullPolicy is Always, or the image is not cached on that node.

Can I see which image digest is running? kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[0].imageID}'.

Next steps #

If the pod gets past the pull and still fails, continue with CrashLoopBackOff. For pods that never start at all, read Pending. The complete method is in How to debug Kubernetes.

#Kubernetes #Troubleshooting #Imagepullbackoff #Containers