ImagePullBackOff means the kubelet could not pull your Pod's container image and is waiting, with a growing delay, before it tries again. The container never starts. The cause is almost always in the events of kubectl describe pod, in the "Failed to pull image" message. The usual suspects: a wrong image name or tag, a private registry without imagePullSecrets, the Docker Hub pull rate limit, network or DNS trouble on the node, an image that doesn't ship your node's CPU architecture, and a registry certificate the node doesn't trust. ErrImagePull is a single failed attempt; ImagePullBackOff is the wait that follows.
I've been running Kubernetes clusters since 2017, today mostly k3s on Hetzner Cloud. I'm a CNCF Kubestronaut and wrote the Kubernetes Practical Guide published by Rheinwerk Computing. All my Kubernetes articles are on the Kubernetes page.
What ImagePullBackOff means in Kubernetes
Per the Kubernetes docs, a container has three possible states: Waiting, Running and Terminated. While the image is missing, it's Waiting, and the Reason field tells you why, in this case ImagePullBackOff. The Pod stays in the Pending phase, which, per the docs, includes the time spent downloading images. More on phases and states in Kubernetes Pod: What Is a Pod?.
Unlike a CrashLoopBackOff, your code never ran, so there are no logs. The answer is in the events.
ErrImagePull vs. ImagePullBackOff
Both describe the same problem at two points in time. In the kubelet source, ErrImagePull is defined as a general image pull error; you see it right after a failed attempt. Then a back-off timer starts. If the kubelet is asked for the image again while it runs, it reports ImagePullBackOff and writes the previous error into the container's Message field, shown under State: Waiting in kubectl describe pod. That's why the STATUS column in kubectl get pods flips between the two.
The Kubernetes docs cap the wait at 300 seconds, or 5 minutes. In the source, it starts at 10 seconds and doubles with each failure, so a long-stuck Pod only retries every few minutes.
A few related reasons are worth recognizing on sight:
Diagnosis: read kubectl describe and the events
Three commands are enough:
kubectl get pods -n shop
kubectl describe pod api-7d9c6b5f8-x2k4q -n shop
kubectl events --for pod/api-7d9c6b5f8-x2k4q -n shop
In kubectl describe pod, the container section shows State: Waiting with Reason: ImagePullBackOff. The events come at the very bottom. Schematically, shortened and with an example image, the sequence looks like this:
Events:
Type Reason Message
---- ------ -------
Normal Pulling Pulling image "registry.example.com/shop/api:1.4.2"
Warning Failed Failed to pull image "registry.example.com/shop/api:1.4.2": <container runtime error>
Warning Failed Error: ErrImagePull
Normal BackOff Back-off pulling image "registry.example.com/shop/api:1.4.2"
Warning Failed Error: ImagePullBackOff
What matters is the text after "Failed to pull image": what the container runtime got back from the registry, such as an unknown image, a denied login, a timeout or a certificate error. Match it to one of the causes below. More everyday commands are in kubectl Commands: The Essentials.
Often missed: the container runtime on the node pulls the image, not your Pod, using the node's network, DNS and certificates. Testing from another Pod tells you little. With node access, test there with crictl pull <image>, usually as root, or k3s crictl pull <image> on k3s. For a private registry, crictl ignores the imagePullSecret; pass --creds <user>:<token>, or the test fails even with a healthy registry. Without node access, docker pull <image> on your machine is the first check, and the one the Kubernetes docs suggest.
The most common causes and how to fix them
Wrong image name or tag
The Kubernetes docs name a failed image pull as the most common reason for Pods stuck in Waiting and give three checks: is the name right, was the image pushed, can you pull it by hand? Watch what Kubernetes fills in silently: busybox becomes docker.io/library/busybox:latest. Leave out the registry, and Kubernetes asks Docker Hub.
For production, the docs advise against :latest and recommend a meaningful tag such as v1.42.0 or a digest. A digest never changes; a tag can later point to a different image.
Private registry without imagePullSecrets
A private registry needs credentials. The docs recommend a Secret of type kubernetes.io/dockerconfigjson that you reference in the Pod as imagePullSecrets:
kubectl create secret docker-registry regcred -n shop \
--docker-server=registry.example.com \
--docker-username=<user> \
--docker-password=<token>
spec:
imagePullSecrets:
- name: regcred
containers:
- name: api
image: registry.example.com/shop/api:1.4.2
The Secret has to live in the same namespace as the Pod. If the events show FailedToRetrieveImagePullSecret, Kubernetes can't find a Secret with that name. Per the docs, credentials typed on the command line may end up in your shell history. So create the Secret from a file instead (kubectl create secret generic regcred --from-file=.dockerconfigjson=<path> --type=kubernetes.io/dockerconfigjson, described on the same docs page), and use a read-only token rather than your password. If every Pod in a namespace uses the same registry, attach the Secret to the Pods' ServiceAccount, usually default. New Pods with that ServiceAccount pick it up automatically, existing ones don't. More in ConfigMap and Secret in Kubernetes.
Docker Hub pull rate limit
According to the Docker docs (as of October 28, 2026), Docker Hub allows unauthenticated users 100 pulls per 6 hours per IPv4 address or IPv6 /64 subnet, and authenticated Personal accounts 200. Paid plans have no pull limit but a fair use policy. Nodes pulling anonymously through the same public IPv4 address share one allowance, and a multi-arch image counts as one pull per architecture.
Once you hit the limit, Docker Hub answers with status 429 and "You have reached your pull rate limit". The ratelimit-limit and ratelimit-remaining headers of a HEAD request show where you stand, and per Docker a HEAD request doesn't count as a pull. Fixes: pull authenticated with an imagePullSecret for https://index.docker.io/v1/, run a mirror, and use fixed tags with imagePullPolicy: IfNotPresent.
Network, DNS and proxy
If the node can't reach the registry, the error usually names the host and the network error. Typical culprits are a firewall, a proxy the container runtime doesn't know about, or a hostname the node's DNS can't resolve. In my training sessions, this happens often on company laptops: the cluster starts, the Pods stay in ImagePullBackOff, and almost always a proxy or firewall is blocking the image download. How to deal with it locally is in Local Kubernetes: A Cluster on Your Machine. Check from the node first, then the cluster.
Wrong CPU architecture
Per the Kubernetes docs, a registry can serve an image index with one image per architecture. If the node's architecture is missing, say linux/arm64 on a Raspberry Pi, containerd stops with "no match for platform in manifest". docker buildx imagetools inspect <image> shows which platforms an image has. If an image is mislabeled instead, the node pulls it, the container exits immediately, and you end up in CrashLoopBackOff. More in Kubernetes on the Raspberry Pi.
Registry certificate
If your registry uses a certificate from an internal CA, the pull fails at the TLS handshake. The CA belongs on every node: with containerd as the ca field in a hosts.toml under /etc/containerd/certs.d/<registry-host>/, which only takes effect if config_path = "/etc/containerd/certs.d" is set in the containerd configuration, in k3s as ca_file under tls in /etc/rancher/k3s/registries.yaml, followed, per the docs, by a k3s restart on each node. Switching verification off (insecure_skip_verify or skip_verify) is a quick test at most.
After the fix: wait or restart?
After the fix, the kubelet retries on its own, in the worst case only after 5 minutes. In the source, the back-off is tracked per Pod and image, so a new Pod starts without any wait. If the fix was outside the manifest (image pushed, Secret created, network fixed), delete the Pod if a Deployment recreates it, or restart it with kubectl rollout restart deployment api -n shop. If you changed the manifest, Kubernetes rolls out new Pods anyway. How rollouts behave is covered in Rolling Update and Rollback in Kubernetes.
How to prevent ImagePullBackOff
Four habits prevent most cases:
- Fixed tags or digests instead of
:latest, so nodes don't re-pull for no reason.
- Credentials on the ServiceAccount, so nobody forgets the
imagePullSecret in a manifest.
- Your own registry or a mirror for base images, independent of the Docker Hub rate limit.
- Builds for every architecture your nodes run, plus a pipeline check that the image with exactly that tag exists. What such a pipeline looks like is in Kubernetes CI/CD: the Pipeline.
Frequently asked questions
What does ImagePullBackOff mean?
The kubelet couldn't pull the image from the registry and is waiting before the next attempt, with a growing pause of up to 5 minutes. The container doesn't run, and the Pod stays Pending.
What's the difference between ErrImagePull and ImagePullBackOff?
ErrImagePull reports a failed attempt; ImagePullBackOff is the wait that follows. The cause is the same and sits in the events of kubectl describe pod.
How do I fix ImagePullBackOff with a private registry?
Create a Secret with kubectl create secret docker-registry in the Pod's namespace and reference it under imagePullSecrets. If you see FailedToRetrieveImagePullSecret, the name or the namespace is wrong.
Why does docker pull work on my machine but not in the cluster?
Your machine and the node differ in credentials, network, certificates and often architecture. Test on the node itself, for example with crictl pull, adding --creds for a private registry.
Where to go next
ImagePullBackOff happens before the container ever starts. If the image runs but the container keeps crashing, CrashLoopBackOff: Debugging Pods gets you out. If the kernel kills it for running out of memory, read Kubernetes OOMKilled: Detect and Fix.
Next time: kubectl describe pod, read the text after "Failed to pull image", find the cause in the table above. The fundamentals of Pods and the command line are in my Kubernetes Practical Guide (Rheinwerk Computing).