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

Kubernetes Error Guide: 'pull access denied' Private Registry Auth Failure

Fix 'pull access denied, repository does not exist or may require docker login' in Kubernetes by wiring a working imagePullSecret to the pod's service account.

  • #kubernetes-helm
  • #troubleshooting
  • #errors
  • #registry

Exact Error Message

When the kubelet tries to pull from a private registry without valid credentials, the pull fails with pull access denied and the pod enters ErrImagePull then ImagePullBackOff:

Events:
  Type     Reason     Age                From     Message
  ----     ------     ----               ----     -------
  Normal   Pulling    20s (x3 over 58s)  kubelet  Pulling image "registry.acme.io/payments/api:1.4.0"
  Warning  Failed     19s (x3 over 57s)  kubelet  Failed to pull image "registry.acme.io/payments/api:1.4.0": rpc error: code = Unknown desc = failed to pull and unpack image "registry.acme.io/payments/api:1.4.0": failed to resolve reference "registry.acme.io/payments/api:1.4.0": pull access denied, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
  Warning  Failed     19s (x3 over 57s)  kubelet  Error: ErrImagePull
  Normal   BackOff    5s  (x4 over 56s)  kubelet  Back-off pulling image "registry.acme.io/payments/api:1.4.0"

The canonical CLI form is:

Error response from daemon: pull access denied for registry.acme.io/payments/api, repository does not exist or may require 'docker login'

The phrase may require 'docker login' is the giveaway: the registry refused the request because the kubelet presented no credentials or invalid ones.

What the Error Means

Private registries refuse anonymous reads. They return a generic denied to anyone who is not authenticated — deliberately ambiguous so attackers cannot distinguish “this repo does not exist” from “you are not allowed in.” Kubernetes surfaces that ambiguity verbatim: repository does not exist or may require docker login.

In a cluster, the kubelet does not have your laptop’s ~/.docker/config.json. It obtains registry credentials from an imagePullSecret of type kubernetes.io/dockerconfigjson, which must be referenced either by the pod spec (imagePullSecrets) or by the pod’s service account. If no such secret is attached, or it holds wrong/expired credentials, or it points at a different registry host, the kubelet pulls anonymously and the registry says denied.

So this error is almost always a credentials-wiring problem, not a missing image. The fix is to create a correct pull secret and attach it where the pod will actually look.

Common Causes

  • No imagePullSecret at all — a private image with a pod and service account that reference no pull secret.
  • Secret in the wrong namespace — pull secrets are namespaced; a secret in default does nothing for a pod in prod.
  • Wrong registry host in the secret — the .dockerconfigjson auths key must match the image’s registry exactly (registry.acme.io vs https://registry.acme.io/v1/).
  • Expired or rotated credentials — a token or robot-account password expired, or the password was rotated without updating the secret.
  • Secret not attached — the secret exists but neither the pod nor its service account lists it under imagePullSecrets.
  • Wrong secret type — created as Opaque instead of kubernetes.io/dockerconfigjson, so the kubelet ignores it.
  • Insufficient scope — the credentials are valid but the robot account/token lacks pull permission on that specific repository.

How to Reproduce the Error

Reference any private image with no pull secret configured:

apiVersion: v1
kind: Pod
metadata:
  name: pull-denied-demo
spec:
  containers:
    - name: app
      image: registry.acme.io/payments/api:1.4.0   # private, no secret attached
kubectl apply -f pull-denied-demo.yaml
kubectl get pod pull-denied-demo
NAME               READY   STATUS             RESTARTS   AGE
pull-denied-demo   0/1     ImagePullBackOff   0          22s

kubectl describe pod pull-denied-demo shows pull access denied ... may require 'docker login'.

Diagnostic Commands

# Read the exact error and the image reference
kubectl describe pod <POD> | grep -A8 Events

# Does the pod (or its service account) reference any pull secret?
kubectl get pod <POD> -o jsonpath='{.spec.imagePullSecrets}{"\n"}'
kubectl get pod <POD> -o jsonpath='{.spec.serviceAccountName}{"\n"}'
kubectl get sa <SERVICE_ACCOUNT> -o jsonpath='{.imagePullSecrets}{"\n"}'

# Is the secret the right type and in the right namespace?
kubectl get secret <SECRET> -o jsonpath='{.type}{"\n"}'
kubectl get secret <SECRET> -n <NAMESPACE>

# Inspect which registry hosts the secret actually authenticates
kubectl get secret <SECRET> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq '.auths | keys'

# Verify the credentials work from a machine, read-only
skopeo inspect --creds '<USER>:<PASS>' docker://registry.acme.io/payments/api:1.4.0

The jq '.auths | keys' output must contain the exact registry host from the failing image reference.

Step-by-Step Resolution

1. Confirm the registry host. Extract the host from the image reference in the describe output. The pull secret’s auths must key on that exact host.

2. Create a docker-registry secret in the pod’s namespace.

kubectl create secret docker-registry acme-pull \
  --docker-server=registry.acme.io \
  --docker-username=<ROBOT_USER> \
  --docker-password=<ROBOT_TOKEN> \
  --namespace=prod

3. Attach it to the pod or the service account. Per-pod:

spec:
  imagePullSecrets:
    - name: acme-pull

Or cluster-wide for everything using that service account:

kubectl patch serviceaccount default -n prod \
  -p '{"imagePullSecrets":[{"name":"acme-pull"}]}'

4. Verify the secret type. It must be kubernetes.io/dockerconfigjson. If you created it as Opaque, delete and recreate with kubectl create secret docker-registry.

5. Test the credentials independently. Run skopeo inspect --creds (above). If that fails too, the credentials themselves are wrong or unscoped — rotate the robot token and grant it pull on the repository.

6. Recreate the pod and watch it pull.

kubectl delete pod <POD>
kubectl get pod <POD> -w

7. For namespace sprawl, automate replication of the pull secret into every namespace that needs it (e.g. a controller or a sync job), since secrets do not cross namespace boundaries.

Prevention and Best Practices

  • Attach pull secrets to service accounts, not individual pods, so every workload in a namespace inherits them automatically.
  • Use robot/service accounts with read-only scope per repository instead of human credentials, and rotate them on a schedule.
  • Replicate the pull secret into every namespace that runs private images — a small sync job prevents the “works in default, fails in prod” trap.
  • On managed clusters, prefer the cloud-native identity path (IAM roles for ECR, workload identity for GCR/Artifact Registry) so no static secret exists to expire.
  • Alert on ImagePullBackOff so an expired token surfaces before a scaling event multiplies the failure.
  • Keep the registry host string consistent between manifests and secrets — no scheme, no trailing path.

Frequently Asked Questions

Why does the message say the repository might not exist when I know it does? Private registries return a deliberately ambiguous denied to unauthenticated callers so they cannot probe which repos exist. The repository is almost always fine — the kubelet just presented no valid credentials.

I have an imagePullSecret but it is still failing. Why? Three common reasons: the secret is in a different namespace than the pod, the pod/service account does not actually reference it, or the secret’s auths keys on a different registry host than the image. Check all three with the diagnostic commands above.

Does the pull secret need to be in every namespace? Yes. ImagePullSecrets are namespaced and do not propagate. A pod in prod cannot use a secret in default. Replicate the secret into each namespace or attach it via that namespace’s service account.

What is the difference between attaching to a pod vs a service account? A pod-level imagePullSecrets applies only to that pod. Attaching to the service account makes every pod that uses that account inherit the secret automatically — far less error-prone at scale.

Should I use static secrets or cloud IAM? On managed clusters, prefer cloud-native identity (ECR/GCR/ACR workload identity). It removes the static credential entirely, so there is nothing to expire or leak. Static docker-registry secrets are best for self-hosted or third-party registries.

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.