# Kubernetes ImagePullBackOff and ErrImagePull: Causes, Debugging and Fixes

> Fix ImagePullBackOff and ErrImagePull in Kubernetes: wrong image or tag, private registry credentials, ECR tokens, rate limits, local images and network problems.

- Source: https://www.itwonderlab.com/kubernetes-imagepullbackoff/
- Published: 2026-06-18
- Updated: 2026-06-18
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## What ImagePullBackOff means

Before a container can start, the kubelet asks the container runtime ([containerd](https://www.itwonderlab.com/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**.

```text
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

```bash
kubectl describe pod <pod>
```

Look at the end of the **Events**:

```text
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`:

```bash
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:

```bash
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
```

```yaml
    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:

```bash
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:

```bash
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](https://www.itwonderlab.com/terraform-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](https://www.itwonderlab.com/rancher-desktop-push-images-aws-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.

```bash
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](https://www.itwonderlab.com/kind-local-kubernetes-cluster/) and [Rancher Desktop Kubernetes](https://www.itwonderlab.com/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:

```bash
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](https://www.itwonderlab.com/docker-buildkit-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

```bash
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](https://www.itwonderlab.com/docker-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](https://www.itwonderlab.com/kubernetes-crashloopbackoff/). For pods that never start at all, read [Pending](https://www.itwonderlab.com/kubernetes-pod-pending/). The complete method is in [How to debug Kubernetes](https://www.itwonderlab.com/how-to-debug-kubernetes/).
