Kubernetes "failed to pull image" private registry fix
Fixes Kubernetes ImagePullBackOff and failed-to-pull-image errors from private registries. Use when pods can't pull images, you see 401/403 from the registry, or imagePullSecrets are missing or wrong. Triggers: ImagePullBackOff, ErrImagePull, unauthorized, no basic auth credentials, manifest unknown. Not for: image build failures, pods that pull fine then crash, public registry rate limits (see the Docker Hub variant).
TL;DR
Kubernetes cant pull from a private registry because it has no credentials, so create a docker-registry secret and reference it as an imagePullSecret in the pod. Most failed-to-pull-image errors are a missing or expired secret, or a typo in the image name. Fix the credentials, the pod pulls.
The error
Failed to pull image "registry.example.com/app:v1": rpc error: code = Unknown desc = failed to pull and unpack image: failed to resolve reference: pull access deniedAlso: ImagePullBackOff, ErrImagePull, unauthorized: authentication required, no basic auth credentials.
Use this when
- pod status is ImagePullBackOff or ErrImagePull
- describe pod shows
Failed to pull imageorpull access denied - you just moved an image to a private registry
- registry credentials were rotated recently
Not for
- image build errors (those happen before any pull)
- pods that pull fine and then crash (CrashLoopBackOff)
- DNS or network failures reaching the registry (test connectivity first)
Steps
- Get the exact failure:
kubectl describe pod [pod-name] -n [namespace]Expected: Events show Failed to pull image with the registry's reason. unauthorized or pull access denied means credentials; manifest unknown or not found means the tag or repo name is wrong.
- Check what the pod is trying to pull:
kubectl get pod [pod-name] -n [namespace] -o jsonpath='{.spec.containers[*].image}'
kubectl get pod [pod-name] -n [namespace] -o jsonpath='{.spec.imagePullSecrets}'Expected: the full image path including registry host. If imagePullSecrets is empty and the registry is private, thats your problem.
- Create the registry secret value ```bash
kubectl create secret docker-registry regcred \ --docker-server=[registry-host] \ --docker-username=[username] \ --docker-password value [your value] \ -n [namespace]
Expected: `secret/regcred created`. Use a token or robot account, not a personal password, so rotations dont break deploys.
4. Attach it to the workload:
```yaml
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/app:v1Apply, then verify the pod pulls:
kubectl get pod [pod-name] -n [namespace] -wExpected: pod moves from ImagePullBackOff to Running. If it still fails, the secret contents are wrong, not the wiring.
- Verify the secret actually works by testing the credentials yourself:
docker login [registry-host] -u [username]Expected: Login Succeeded. If login fails here, the credentials are bad at the registry, not in Kubernetes.
- For a whole namespace, skip per-pod secrets:
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "regcred"}]}' -n [namespace]Expected: every new pod in the namespace inherits the secret. Existing pods need a restart to pick it up.
Variant: manifest unknown or repository not found
The tag doesnt exist or the repo name is misspelled. List tags at the registry and fix the image field. Credentials are fine in this case.
Variant: works on Docker Hub but fails on a mirror or proxy
Point imagePullSecrets at the mirror's credentials and make sure the image path uses the mirror host. Also check the mirror actually has the tag cached.
Variant: ECR, GCR, or ACR with expiring tokens
Cloud registry tokens expire (ECR: 12 hours). Use the cloud credential helper or an external-secrets sync that refreshes the secret, not a static token.
Why it happens
The kubelet pulls images with no credentials by default. Public images work; anything private gets a 401 from the registry. imagePullSecrets are the only way to hand the kubelet credentials, and theyre per-namespace, so a secret in the wrong namespace silently does nothing.
Edge cases
- Secrets are namespace-scoped: the secret and the pod must live in the same namespace.
imagePullPolicy: Alwayswith a private registry re-authenticates every pull; a rotated credential breaks running pods on reschedule.- Some registries rate-limit anonymous pulls; even public images can need a secret to stay under the limit.
- On managed clusters, the nodes service account may already have pull rights to the cloud provider's registry; check that before adding secrets.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_9A0xt1e8rF6hDW0gximBzQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.