Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 9 min read

Kubernetes Error Guide: 'ErrImageNeverPull' Missing Local Image

Fix the ErrImageNeverPull error: load the image onto the node, correct imagePullPolicy Never, and align tags so pods using preloaded images start cleanly.

  • #kubernetes-helm
  • #troubleshooting
  • #errors
  • #images

Exact Error Message

NAME                       READY   STATUS              RESTARTS   AGE
demo-79b8c5d6f4-q9w2x      0/1     ErrImageNeverPull   0          25s

# kubectl describe pod demo-79b8c5d6f4-q9w2x
Events:
  Type     Reason             Age              From     Message
  ----     ------             ----             ----     -------
  Warning  ErrImageNeverPull  6s (x4 over 25s) kubelet
    Container image "myapp:dev" is not present with pull policy of Never

What the Error Means

ErrImageNeverPull means the pod’s container uses imagePullPolicy: Never, which tells the kubelet to use only an image that already exists on the node — never to contact a registry. The kubelet looked for that exact image reference in the node’s local image store, did not find it, and because the policy forbids pulling, it gives up immediately with ErrImageNeverPull.

This is a deliberate behavior, common in local development (kind, minikube, k3d) and air-gapped clusters where images are side-loaded onto nodes rather than pulled from a registry. The error simply means “you told me not to pull, but the image is not here.” It is never a network or auth problem.

Common Causes

  • The image was never loaded onto the node that the pod scheduled to, or was loaded onto a different node.
  • A tag mismatch. The image on the node is myapp:latest but the pod requests myapp:dev; the kubelet matches by exact reference.
  • imagePullPolicy: Never left in place after moving from local dev to a real registry, where the image should be pulled instead.
  • In multi-node clusters, the image was preloaded on only one node but the pod scheduled elsewhere.
  • The image was built inside the wrong container runtime — for example built into Docker’s store while the node uses containerd, so the kubelet cannot see it.

How to Reproduce the Error

apiVersion: v1
kind: Pod
metadata:
  name: never-pull
spec:
  containers:
    - name: app
      image: myapp:dev          # not present on the node
      imagePullPolicy: Never
kubectl apply -f never-pull.yaml
kubectl get pod never-pull
# STATUS: ErrImageNeverPull

Diagnostic Commands

# Confirm the reason and the node the pod landed on
kubectl get pod <POD> -o wide
kubectl get pod <POD> -o jsonpath='{.status.containerStatuses[0].state.waiting.reason}{"\n"}'

# Confirm the pull policy and exact image reference
kubectl get pod <POD> -o jsonpath='{.spec.containers[0].imagePullPolicy} {.spec.containers[0].image}{"\n"}'

# List images present on the node (containerd)
crictl images | grep myapp

# List images present on the node (Docker runtime)
docker images | grep myapp

# kind / minikube: list what is loaded into the cluster nodes
kind get nodes
docker exec <KIND_NODE> crictl images | grep myapp

# See which node has the image vs where the pod scheduled
kubectl get nodes

Step-by-Step Resolution

1. Confirm this is a “not present” problem, not a typo. Check the exact image reference and the policy:

kubectl get pod <POD> -o jsonpath='{.spec.containers[0].image} policy={.spec.containers[0].imagePullPolicy}{"\n"}'

2. Load the image onto the node(s). For kind or minikube, side-load the locally built image so every node has it:

# kind
kind load docker-image myapp:dev --name <CLUSTER>

# minikube
minikube image load myapp:dev

For a generic containerd node, import the saved image:

docker save myapp:dev -o myapp.tar
# copy myapp.tar to the node, then:
ctr -n k8s.io images import myapp.tar

3. Verify the tag matches exactly. The kubelet matches the full reference. If the node has myapp:latest but the pod wants myapp:dev, either retag the image or update the pod to the present tag:

crictl images | grep myapp

4. If you actually want Kubernetes to pull from a registry, change the policy. Set it to IfNotPresent (or Always) and ensure the image is pushed to a reachable registry:

kubectl patch deployment <DEPLOY> --type=json \
  -p='[{"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"}]'

5. Recreate the pod and confirm it starts. Once the image is present (or the policy is fixed), the pod proceeds to Running:

kubectl delete pod <POD>   # if owned by a controller it is recreated
kubectl get pods -l app=<APP> -w

Prevention and Best Practices

  • Use imagePullPolicy: Never only when you control image side-loading on every node; otherwise prefer IfNotPresent.
  • In multi-node local clusters, load images to all nodes (kind load ... --name) or constrain the pod with a node selector to a node that has the image.
  • Keep tags explicit and consistent between your build step and your manifests — avoid relying on latest for side-loaded images.
  • Build into the same runtime the node uses (containerd for most modern clusters), or load via crictl/ctr so the kubelet can see the image.
  • For air-gapped environments, automate image side-loading as part of node provisioning so freshly added nodes are not missing images. See more in Kubernetes & Helm guides.
  • ErrImagePull / ImagePullBackOff — the policy allowed a pull but the registry fetch failed.
  • InvalidImageName — the image reference itself is malformed and cannot be parsed.
  • ImageInspectError — the image is present but the runtime could not read its metadata.
  • CreateContainerError — the image is available but the runtime failed to create the container.

Frequently Asked Questions

Why does the kubelet refuse to pull when the image is clearly available in my registry? Because imagePullPolicy: Never is an explicit instruction to use only local images. The kubelet will not contact any registry under this policy, even if the image exists remotely. Change the policy to IfNotPresent or Always to allow a pull.

I loaded the image but still get the error. What now? You likely loaded it onto a different node than the one the pod scheduled to, or the tag differs. Run crictl images on the actual node from kubectl get pod -o wide and compare the exact reference with .spec.containers[0].image.

Does building with docker build make the image visible to a containerd-based node? Not automatically. Docker and containerd maintain separate image stores. Use kind load, minikube image load, or ctr -n k8s.io images import to place the image where the kubelet looks.

Is ErrImageNeverPull ever a transient error that resolves on its own? No. Unlike ImagePullBackOff, it will not self-heal because the kubelet never retries a registry pull. You must load the image or change the pull policy.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.