TL;DR
ImagePullBackOffmeans the kubelet cannot pull the container image and is retrying with exponential backoff.ErrImagePullis the failed attempt;ImagePullBackOffis the waiting state between retries.- The fix always starts with
kubectl describe podand the Events section. The error string there maps directly to the cause: wrong tag, missingimagePullSecrets, rate limit, or network failure. - Wrong image name or tag is the fastest fix: correct
spec.containers[].image, then restart the workload. - Private registries need a
kubernetes.io/dockerconfigjsonSecret referenced inspec.imagePullSecretsor on the ServiceAccount. - After any fix, confirm with
kubectl get podsshowingRunningand1/1Ready.
You merged a Deployment, ran kubectl get pods, and every replica is stuck in ImagePullBackOff. The rollout looks green in GitOps, but nothing is serving traffic. You open the dashboard, see a red pod, and still do not know whether the image name is wrong, the registry password expired, or the cluster lost network access to Docker Hub.
In this article, you will reproduce the failure in a sandbox namespace, read the exact error from pod Events, fix the two most common causes (bad image reference and missing registry credentials), and verify the pod reaches Running with screenshots at each step.
Note
What you need: kubectl 1.28+, access to a cluster (kind, minikube, EKS, GKE, or AKS), and permission to create namespaces, Secrets, and Deployments in a non-production sandbox. The walkthrough uses namespace demo-app.
Why pods get stuck in ImagePullBackOff
Kubernetes schedules the pod onto a node. The kubelet asks the container runtime (containerd or CRI-O) to pull the image from the registry in spec.containers[].image. When that pull fails, you see ErrImagePull on the first attempts. After repeated failures, the kubelet backs off and the pod status becomes ImagePullBackOff.
That status does not mean the pod crashed. It means the image never arrived. The application container never started.
| Status | What it means |
|---|---|
ErrImagePull | A pull attempt failed right now |
ImagePullBackOff | Pull failed; kubelet is waiting before the next retry |
InvalidImageName | Malformed image string in the pod spec |
ImageInspectError | Runtime could not inspect the image after pull |
Most production incidents fall into four buckets:
| Error pattern in Events | Typical cause | Fix |
|---|---|---|
not found / manifest unknown | Wrong repository or tag | Correct the image field |
unauthorized / authentication required | Missing or bad imagePullSecrets | Create or update registry Secret |
toomanyrequests | Docker Hub anonymous rate limit | Authenticate with pull Secret |
i/o timeout / no such host | DNS, firewall, or registry outage | Fix network path to registry |
How to read the error in pod Events
Do not guess from the Deployment YAML alone. Start with the pod status, then read Events.
Deploy a deliberately broken image so you can see the failure state. This manifest references a tag that does not exist on Docker Hub:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: nginx:does-not-exist-2026
ports:
- containerPort: 80Create the namespace and apply the manifest:
kubectl create namespace demo-app
kubectl apply -f broken-deployment.yamlList pods until the status shows ImagePullBackOff:
kubectl get pods -n demo-app -wYou should see output like this:
Describe the pod and scroll to Events. The Failed line contains the registry error string you need:
kubectl describe pod -n demo-app -l app=apiIn this example, not found means the tag does not exist. The fix is not to restart the node. It is to correct the image reference.
How to fix a wrong image name or tag
Confirm the image exists before you change the cluster. For public images on Docker Hub:
docker pull nginx:1.25-alpineIf that fails locally, the tag is wrong or the registry is unreachable from your network.
Patch the Deployment to a valid image:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: nginx:1.25-alpine
ports:
- containerPort: 80Apply the fix and restart the rollout so new pods pull immediately:
kubectl apply -f fixed-deployment.yaml
kubectl rollout restart deployment/api -n demo-app
kubectl rollout status deployment/api -n demo-appWatch the pod transition to Running:
kubectl get pods -n demo-appIf the pod still shows ImagePullBackOff, describe it again. A typo in the new tag produces the same error string with a different image name.
How to fix private registry authentication
When the Events line says unauthorized or authentication required, the image reference is probably correct but the cluster has no credentials for a private registry.
Run kubectl describe pod on the failing workload. The Events section for a missing pull Secret looks like this:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 21s kubelet Failed to pull image "registry.example.com/api:v2": rpc error: code = Unknown desc = failed to pull and unpack image "registry.example.com/api:v2": failed to resolve reference "registry.example.com/api:v2": pull access denied, repository does not exist or may require authorization: server message: unauthorized: authentication requiredCreate the registry pull Secret
Replace the registry host, username, and password with your values. The Secret must live in the same namespace as the pod.
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=ci-bot \
--docker-password='YOUR_TOKEN' \
--docker-email=ci-bot@example.com \
-n demo-appVerify: kubectl get secret regcred -n demo-app shows type kubernetes.io/dockerconfigjson.
Attach the Secret to the workload
Reference the Secret in the pod spec (or on the ServiceAccount every pod in the namespace uses):
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
imagePullSecrets:
- name: regcred
containers:
- name: api
image: registry.example.com/api:v2
ports:
- containerPort: 80Apply and restart:
kubectl apply -f deployment-with-pull-secret.yaml
kubectl rollout restart deployment/api -n demo-appVerify: kubectl describe pod -n demo-app -l app=api shows Pulled and Created events without unauthorized.
Patch the ServiceAccount when many Deployments share a namespace
Attaching the Secret to the default ServiceAccount avoids repeating imagePullSecrets in every manifest:
kubectl patch serviceaccount default -n demo-app \
-p '{"imagePullSecrets": [{"name": "regcred"}]}'Verify: kubectl get sa default -n demo-app -o yaml lists regcred under imagePullSecrets.
Warning
Rotating registry tokens requires recreating the Secret (kubectl delete secret regcred then create again) or using a secrets operator. Updating only the Deployment image does not refresh expired credentials.
How to verify the pod is healthy
A fixed pull ends with three signals:
- Pod phase:
kubectl get pods -n demo-appshowsSTATUS=RunningandREADY=1/1. - Events:
kubectl describe podshowsPulled,Created, andStartedwithout newFailedpull events. - Application check: Port-forward or hit the Service to confirm the process responds:
kubectl port-forward -n demo-app deployment/api 8080:80
curl -I http://127.0.0.1:8080You should receive an HTTP response from Nginx (or your application health endpoint).
When these fixes do not apply
ImagePullBackOff is always an image delivery problem, but not every delivery problem is a wrong tag or missing Secret.
- Self-signed registry TLS: The runtime may reject the certificate before auth runs. You need CA trust configured on nodes (containerd
certs.dor cluster-specific registry config), not only animagePullSecretsentry. imagePullPolicy: Neverwith a missing local image: The kubelet will not pull from a registry. The image must already exist on the node under the exact name.- Architecture mismatch: Pull succeeds but the container fails to start with
exec format error. That is a different failure mode after pull; fix the image build target platform. - Air-gapped clusters: No route to public registries. Mirror images to an internal registry and update all manifests to that host.
For container networking after the pod is running, see Docker ports vs expose for how port publishing differs between Docker and Kubernetes Service types.
For authoritative reference, see the Kubernetes docs on pulling an image from a private registry and debugging pods.

