Fix Kubernetes ImagePullBackOff and ErrImagePull

Diagnose and fix Kubernetes ImagePullBackOff and ErrImagePull using Pod events, image references, pull Secrets, policies, TLS, DNS, and platform checks.

Published

Updated

Read time 13 min read

Reviewed byDeepak Prasad

Kubernetes ImagePullBackOff and ErrImagePull troubleshooting with kubectl describe events and registry fixes
Tested on Rocky Linux 10.2 (Red Quartz) workstation
Package kubectl 1.36.3
Applies to Bash-compatible Linux or macOS shell with kubectl configured; any compatible Kubernetes cluster
Cert prep CKA · CKAD · CKS
Lab environment Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm
Privilege Normal user (no sudo required on the workstation)
Node access May be required when registry DNS, TLS trust, proxy settings, or container-runtime pulls must be tested from the assigned worker
Scope ErrImagePull versus ImagePullBackOff, event-driven diagnosis with describe and events, image reference verification, manifest unknown and not found, private registry credentials and imagePullSecrets routing, imagePullPolicy effects, platform and manifest errors, TLS and DNS error patterns, rate limits, verification steps, and error-to-fix tables. Does not cover building images in depth, full registry installation, container-runtime trust store setup, general node networking, CrashLoopBackOff after pull succeeds, application startup debugging, or image signing and admission policy.
Related guides kubectl logs, Events and describe
IMPORTANT
This guide fixes image pull failures—ErrImagePull, ImagePullBackOff, and ErrImageNeverPull when policy blocks a pull. It does not cover containers that pulled successfully and then crash; use Fix Kubernetes CrashLoopBackOff for exit codes and restart loops. When STATUS could be scheduling, volume mount, or image pull, start with the Kubernetes Pod troubleshooting workflow first.

The container never started, so there are no application logs to read. Pod events carry the exact registry or runtime error. This walkthrough creates deliberate pull failures in imagepull-lab, reads those events, and maps each message type to the fix you apply on the node that actually pulls the image—not only on your laptop.


Quick reference

There are no application logs during a pull failure. Read events on the assigned worker node. Replace POD and NS with your Pod name and namespace.

Step Command What to check
1 kubectl get pod POD -n NS -o wide NODE — the kubelet on that worker performs the pull, not your laptop
2 kubectl events -n NS --for pod/POD The Failed to pull image line is the primary signal — see inspect the error
3 kubectl describe pod POD -n NS | grep -A8 '^ app:' Image name, tag, and imagePullPolicy the kubelet is using
4 Match the event text to a fix category not found → image reference; deniedimagePullSecrets; x509 → TLS trust on the node; DNS errors → node resolver — see error-to-fix table
5 kubectl get pod POD -n NS -w After the fix, STATUS should move to Running — see verify the fix

ErrImagePull vs ImagePullBackOff

ErrImagePull means the kubelet tried to pull the image and the attempt failed. The event message includes the registry response: not found, pull access denied, x509 certificate errors, or DNS failures.

ImagePullBackOff means Kubernetes is delaying further pull attempts after repeated failures. The container sits in Waiting with reason ImagePullBackOff while the kubelet backs off. STATUS in kubectl get often shows ImagePullBackOff or briefly ErrImagePull between retries.

Neither status is a Pod phase. Phase is often Pending while the image is missing, or can show other values depending on cluster version and container state reporting. The actionable detail is always the Failed event text and the Image field in describe—not the STATUS label alone.


Prepare the Image-Pull Lab

Create two Pods with bad image references: a missing tag and an invalid repository path.

bash
kubectl apply -f - <<'YAML'
apiVersion: v1
kind: Namespace
metadata:
  name: imagepull-lab
---
apiVersion: v1
kind: Pod
metadata:
  name: bad-tag
  namespace: imagepull-lab
spec:
  containers:
  - name: app
    image: nginx:9.99.99-notexist
---
apiVersion: v1
kind: Pod
metadata:
  name: bad-name
  namespace: imagepull-lab
spec:
  containers:
  - name: app
    image: nginx/not-a-valid-path:1.27.0
YAML

Both Pods schedule to a worker immediately; the kubelet fails on the first pull within seconds.


Inspect the Image-Pull Error

Application logs do not exist yet. Wait until each Pod reports a pull failure, then check STATUS and node placement.

bash
for POD in bad-tag bad-name; do
until REASON=$(kubectl get pod "$POD" \
  -n imagepull-lab \
  -o jsonpath='{.status.containerStatuses[0].state.waiting.reason}' 2>/dev/null) &&
    [[ "$REASON" == "ErrImagePull" || "$REASON" == "ImagePullBackOff" ]]; do
    sleep 2
  done
done
bash
kubectl get pods -n imagepull-lab -o wide

ImagePullBackOff represents a waiting container after failed pulls, with increasingly delayed retries.

Sample output:

output
NAME       READY   STATUS             RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
bad-name   0/1     ImagePullBackOff   0          25s   192.168.5.8    worker01   <none>           <none>
bad-tag    0/1     ImagePullBackOff   0          25s   192.168.5.11   worker01   <none>           <none>

RESTARTS stays 0 because the container never ran. Note NODE—pull and DNS checks must succeed from that worker, not only from your workstation.

Read events for the missing-tag Pod:

bash
kubectl events -n imagepull-lab --for pod/bad-tag

Sample output:

output
LAST SEEN          TYPE      REASON      OBJECT        MESSAGE
25s                Normal    Scheduled   Pod/bad-tag   Successfully assigned imagepull-lab/bad-tag to worker01
21s                Normal    BackOff     Pod/bad-tag   Back-off pulling image "nginx:9.99.99-notexist"
21s                Warning   Failed      Pod/bad-tag   Error: ImagePullBackOff
6s (x2 over 24s)   Normal    Pulling     Pod/bad-tag   Pulling image "nginx:9.99.99-notexist"
4s (x2 over 22s)   Warning   Failed      Pod/bad-tag   Failed to pull image "nginx:9.99.99-notexist": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/library/nginx:9.99.99-notexist": failed to resolve image: docker.io/library/nginx:9.99.99-notexist: not found
4s (x2 over 22s)   Warning   Failed      Pod/bad-tag   Error: ErrImagePull

The Failed to pull image line is the primary signal. ErrImagePull and ImagePullBackOff are follow-on reasons from the same root failure.

Confirm the image string the kubelet is using:

bash
kubectl describe pod bad-tag -n imagepull-lab | grep -A8 '^  app:'

Sample output:

output
app:
    Container ID:   
    Image:          nginx:9.99.99-notexist
    Image ID:       
    Port:           <none>
    Host Port:      <none>
    State:          Waiting
      Reason:       ImagePullBackOff
    Ready:          False

kubectl logs fails because the container never started:

bash
kubectl logs bad-tag -n imagepull-lab 2>&1 | head -1

Sample output:

output
Error from server (BadRequest): container "app" in pod "bad-tag" is waiting to start: trying and failing to pull image

Verify the Image Reference

Before changing credentials or firewall rules, confirm the full reference the Pod spec sends to the kubelet. A complete private-registry example looks like:

text
registry.example.com/team/application:1.2.0

Check each segment:

  • Registry hostname (omit for default docker.io / library/ paths)
  • Repository path and project or namespace segment
  • Image name
  • Tag or digest (@sha256:…)
  • Letter case where the registry treats names as case-sensitive
  • Accidental whitespace or wrong environment variable substitution in CI templates

Compare the live Pod spec to what you pushed:

bash
kubectl get pod bad-tag -n imagepull-lab -o jsonpath='{.spec.containers[0].image}{"\n"}'

Sample output:

output
nginx:9.99.99-notexist

Unqualified nginx:tag resolves to docker.io/library/nginx:tag on most clusters. A typo in tag or path surfaces as not found in events.


Fix not found or manifest unknown

When events include not found, manifest unknown, or name unknown, the registry is reachable, but the tag or repository does not exist.

Verify:

  • The repository path exists in the registry UI or API
  • The tag was pushed to that repository—not only to a fork or alternate project
  • The Deployment or StatefulSet template matches the image you tested locally
  • CI promoted the digest you expect, not an empty or stale tag

Fix the tag or repository in the controller template, then let the controller recreate Pods or run kubectl rollout restart when needed. Building, tagging, and pushing images is covered in Kubernetes container images.

Apply a corrected Pod to confirm the pull path:

bash
kubectl apply -f - <<'YAML'
apiVersion: v1
kind: Pod
metadata:
  name: good-image
  namespace: imagepull-lab
spec:
  containers:
  - name: app
    image: nginx:1.27.0
YAML
bash
kubectl wait --for=condition=Ready pod/good-image -n imagepull-lab --timeout=120s
kubectl get pod good-image -n imagepull-lab

Sample output:

output
NAME         READY   STATUS    RESTARTS   AGE
good-image   1/1     Running   0          8s

Events on the fixed Pod show Pulled, Created, and Started—the pull failure class is resolved.


Fix Private Registry Authentication

Check the Pull Secret

Private registries return pull access denied, unauthorized, or authorization failed: no basic auth credentials when the kubelet has no valid credentials.

Check:

  • A pull Secret exists in the Pod namespace
  • Secret type is kubernetes.io/dockerconfigjson (or legacy kubernetes.io/dockercfg)
  • imagePullSecrets lists the correct Secret name on the Pod or ServiceAccount
  • Username, token, or password is still valid
  • The docker-server hostname in the Secret matches the hostname in the image reference (registry.example.com vs registry.example.com:5000)

Pull Secrets must exist in the Pod namespace and use a supported Docker-config Secret type.

The full setup—creating regcred, attaching it to a ServiceAccount, and testing authenticated pulls—is in Private registry image pulls. This article stops at recognizing auth errors in events and confirming Secret name, namespace, and hostname alignment.

Fix Unauthorized or Access-Denied Errors

When credentials are present but rejected:

  • Confirm the account has pull permission on the repository
  • Rotate expired tokens or robot passwords
  • Match the registry host and optional port in the Secret to the image reference, such as registry.example.com:5000
  • Verify the Pod references the intended Secret—not a similarly named Secret in another namespace
  • For cloud registries, check repository policy and IAM or token scope

A reachable registry with missing credentials often shows:

output
Failed to pull image "192.168.56.108:5000/web:v1.0.0": ... authorization failed: no basic auth credentials
Error: ErrImagePull

Adding the correct imagePullSecrets entry clears that message on the next pull attempt.


Check imagePullPolicy

imagePullPolicy controls whether the kubelet contacts the registry on start:

Policy Behavior
Always Pull (or verify) from the registry every time the container starts
IfNotPresent Pull only when no local image with that name and tag exists on the node
Never Never pull; fail if the image is not already on the node

IfNotPresent can mask a bad tag on one node while another node without the image fails with pull errors. Never produces ErrImageNeverPull when the image is absent locally. With Never, the kubelet uses a local image when present and fails only when it is absent. Use a deliberately unique local-only reference so the failure is reproducible:

bash
kubectl apply -f - <<'YAML'
apiVersion: v1
kind: Pod
metadata:
  name: never-pull
  namespace: imagepull-lab
spec:
  containers:
    - name: app
      image: imagepull-lab.invalid/never-pull:unique-v1
      imagePullPolicy: Never
YAML
bash
kubectl wait pod/never-pull \
  -n imagepull-lab \
  --for=jsonpath='{.status.containerStatuses[0].state.waiting.reason}'=ErrImageNeverPull \
  --timeout=60s
kubectl get pod never-pull -n imagepull-lab

Sample output:

output
NAME         READY   STATUS              RESTARTS   AGE
never-pull   0/1     ErrImageNeverPull   0          15s

Do not switch policy to Never or IfNotPresent merely to hide registry or tagging mistakes—fix the reference or credentials instead.


Fix Architecture and Platform Errors

Nodes pull only manifests that list a compatible platform. When no manifest matches the node OS or architecture, events may report no matching manifest for linux/amd64 or similar.

Check:

  • Node OS and architecture labels:
bash
kubectl get nodes -L kubernetes.io/os,kubernetes.io/arch

Or inspect the assigned node for one failing Pod:

bash
NODE=$(kubectl get pod bad-tag -n imagepull-lab -o jsonpath='{.spec.nodeName}')
kubectl get node "$NODE" -o jsonpath='os={.metadata.labels.kubernetes\.io/os} arch={.metadata.labels.kubernetes\.io/arch}{"\n"}'
  • Platforms the image publishes (docker manifest inspect or registry UI)
  • Whether you need a multi-arch build or an arch-specific tag

If the image pulls but the container fails with exec format error, the binary targets the wrong architecture—that is a startup failure after a successful pull, not ImagePullBackOff. Route those cases to crash-loop and command troubleshooting, not registry auth.


Fix Node-to-Registry Connectivity

Registry TLS Errors

TLS problems appear in pull events before the container starts. Common messages:

  • x509: certificate signed by unknown authority
  • x509: certificate is valid for …, not … (hostname mismatch)
  • Certificate expired errors

Check:

  • Registry certificate chain is complete and not expired
  • Image reference hostname matches the certificate SAN
  • Nodes trust the issuing CA (system store or runtime-specific config)
  • Do not include http:// or https:// in the Pod image field. Use only the registry hostname, optional port, repository, and tag or digest. Configure HTTPS trust or an intentionally insecure registry in the node's container runtime.

Full private-CA installation and container-runtime trust configuration are out of scope here. Use the event text to decide whether to fix the certificate, the hostname in the image string, or node trust—not application code.

Registry DNS and Network Errors

Pull failures from DNS or TCP show messages such as no such host, connection timeouts, or dial tcp errors. The registry must be reachable from the assigned node.

Check from the worker when possible:

  • nslookup or dig for the registry hostname
  • TCP connectivity to the registry port
  • Corporate proxy settings on nodes
  • Firewall paths between node subnets and the registry
  • Registry service health and load balancer backends

Successful docker pull or crictl pull from your laptop does not prove worker nodes can reach the same hostname. Always test from the node listed in kubectl get pod -o wide.

Registry Rate Limits

When events mention rate limiting or too many requests:

  • Authenticate pulls—anonymous limits are often lower
  • Reduce unnecessary pulls (Always on many replicas with mutable tags)
  • Use immutable version tags or digests instead of constantly moving latest
  • Review registry quota and CI retry storms

This is not a product comparison—match the event to auth and pull frequency before opening vendor dashboards.


Verify the Fix

After correcting the image string, Secret, or network path, watch events transition to success:

  • Pulling then Pulled for the expected image reference
  • Created and Started on the container
  • Pod READY matches the number of containers
  • Deployment kubectl rollout status completes when a controller owns the Pod

On the lab fix Pod:

bash
kubectl describe pod good-image -n imagepull-lab | grep -A6 '^Events:'

Sample output:

output
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  8s    default-scheduler  Successfully assigned imagepull-lab/good-image to worker01
  Normal  Pulled     8s    kubelet            spec.containers{app}: Container image "nginx:1.27.0" already present on machine and can be accessed by the pod
  Normal  Created    7s    kubelet            spec.containers{app}: Container created
  Normal  Started    7s    kubelet            spec.containers{app}: Container started

Delete a standalone failed Pod only after you fix the manifest or when you want a controller to recreate it with the updated template. Deleting without fixing the Deployment template recreates the same bad pull.


Error-to-Fix Table

Event message Check first
not found / manifest unknown Repository path, tag, and digest in the Pod spec
pull access denied / unauthorized Registry credentials, token expiry, repository permissions
no basic auth credentials imagePullSecrets name, namespace, and Secret type
x509 certificate errors Registry cert, hostname in image, node or runtime trust
DNS lookup failure Node DNS and registry hostname spelling
no matching manifest Image platforms versus node kubernetes.io/arch
Rate limit exceeded Authentication, tag immutability, pull frequency
ErrImageNeverPull imagePullPolicy: Never with missing local image

Troubleshooting

Symptom Likely cause Fix
kubectl logs returns waiting to pull Container never started Read describe events; ignore logs until pull succeeds
Pull works on laptop, fails on cluster Node network or DNS differs Test from assigned worker node
Only some nodes fail IfNotPresent and stale local image on healthy nodes Fix the image reference and use an immutable tag or digest; recreate the failed Pods after correcting the controller template
Secret exists but pull still denied Wrong docker-server hostname in Secret Match host and port to image reference exactly
ImagePullBackOff after registry outage Backoff timer still active Fix registry; wait for retry or delete Pod after fix
Wrong arch after pull Multi-arch manifest missing platform Publish correct arch or use arch-specific tag

What's Next


References


Summary

Image pull failures never produce application logs—the kubelet stops before the container starts. ErrImagePull marks a failed attempt; ImagePullBackOff means Kubernetes is waiting before retrying. The fix lives in Pod events: the Failed to pull image line names the registry response.

In imagepull-lab, a missing nginx tag produced not found and exit from kubectl logs, while correcting the tag produced Pulled and Started on worker01. Match the message to the category—wrong reference, missing credentials, TLS, DNS, platform manifest, or policy—before tuning unrelated probe or crash-loop settings.

Private registry setup and imagePullSecrets wiring belong in the dedicated registry guide; building and pushing images belong in the container images guide. Once the image pulls, if the container still restarts, switch to CrashLoopBackOff troubleshooting—the pull problem is already solved.


Frequently Asked Questions

1. What is the difference between ErrImagePull and ImagePullBackOff?

ErrImagePull means the kubelet attempted to obtain the container image and the pull failed. ImagePullBackOff means Kubernetes is waiting before retrying after repeated failures. Read Pod events for the underlying registry, authentication, TLS, DNS, network, or platform error. ImagePullBackOff means the image could not be pulled and Kubernetes is retrying with increasing delay.

2. Why are kubectl logs empty during ImagePullBackOff?

The container never started because the image was not pulled. There is no application stdout or stderr yet. Read kubectl describe and kubectl events for the pull error message.

3. Does ImagePullBackOff mean the image name is wrong?

Often, but not always. Missing tags, wrong registry hostname, denied credentials, TLS errors, DNS failures, and platform mismatches can all produce pull failures. Match the event message to the fix category before changing the image string.

4. Where must imagePullSecrets exist?

Pull Secrets are namespace-scoped. The Secret must live in the same namespace as the Pod, and the Pod spec or ServiceAccount must reference the Secret name. The registry hostname in the Secret must match the hostname in the image reference.

5. Can imagePullPolicy IfNotPresent hide a bad tag?

IfNotPresent skips the registry when an image with the same name and tag already exists on the node. A stale local copy can make a Pod start while new nodes fail with pull errors. Always does not fix a wrong tag—it still contacts the registry on each start.

6. Is exec format error an image pull problem?

No. exec format error usually means the image pulled successfully but the binary targets a different CPU architecture than the node. Fix platform or multi-arch publishing, not registry credentials.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)