LearnDevOpsHands-On Labs
Troubleshooting Matrix/pull access denied, repository does not exist or may require authorization
DockerCritical Severity

pull access denied, repository does not exist or may require authorization

Fix "pull access denied, repository does not exist": check the image name, then docker login to the right registry. Covers token scopes, Actions and Kubernetes.

Observed Error Signature / Log Pattern
Error response from daemon: pull access denied for myapp, repository does not exist or may require 'docker login': denie

Incident Overview

Fix "pull access denied, repository does not exist": check the image name, then docker login to the right registry. Covers token scopes, Actions and Kubernetes.

easy fix·6 min read

By Vishvesh Patel·DevOps Engineer·Updated 21 September 2026

BASHDiagnostic / remediation command
Error response from daemon: pull access denied for myapp, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

unauthorized: authentication required

denied: permission_denied: write_package

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

• Check whether the image is public before assuming it is auth

Swap in your own image reference. If an anonymous manifest inspect succeeds, the repository exists and is public, so the problem is the tag or a typo rather than credentials.

• Log in to the registry the image actually names

A bare image name means Docker Hub. Anything with a host prefix such as ghcr.io or an ECR URL needs a login to that host specifically; being logged in to Docker Hub does nothing for it.

• Confirm which registries you hold credentials for

The auths object lists every registry with a stored credential. If the host in your image reference is not a key there, you are pulling anonymously.

pull access denied for X, repository does not exist or may require 'docker login' is deliberately vague. Docker will not tell an anonymous caller whether a private repository exists, because that would let anyone enumerate private image names. So a typo and a permissions problem produce the same message.

Three possibilities, in the order they actually occur:

BASHDiagnostic / remediation command
docker manifest inspect nginx:1.27 > /dev/null && echo reachable

Check whether it is public first

Run it against your own reference. If an anonymous inspect succeeds, the repository is public and exists, so the problem is a typo or a tag that was never pushed. If it fails the same way, move on to credentials.

Be precise about the name. These are all different repositories:

A bare name without a slash resolves into the library/ namespace, which only holds Docker Official Images. Pulling a personal image by bare name therefore always fails, and the message gives no hint that the namespace was the problem.

BASHDiagnostic / remediation command
docker manifest inspect nginx:1.27 > /dev/null && echo reachable

Check where you are logged in

The keys of auths are the registries you have credentials for. If the host in your image reference is not there, you are pulling anonymously. An empty {} value is normal when a credential helper such as desktop or osxkeychain holds the actual secret.

BASHDiagnostic / remediation command
cat ~/.docker/config.json

Fix: Log in to the right registry

Each registry has its own flow. Note that all of them take the token on stdin rather than as an argument, which keeps it out of your shell history and out of the process list.

Docker Hub has required an access token rather than an account password for CLI logins for some time. If your password is being rejected and you are sure it is right, that is why: create a token in account settings and use that.

BASHDiagnostic / remediation command
# Docker Hub, with a Personal Access Token, not your password
echo "$DOCKERHUB_TOKEN" | docker login -u myuser --password-stdin

# GitHub Container Registry, needs read:packages on the token
echo "$GITHUB_TOKEN" | docker login ghcr.io -u myuser --password-stdin

# Amazon ECR, token is valid for 12 hours
aws ecr get-login-password --region eu-west-1 \
  | docker login --username AWS --password-stdin \
    123456789012.dkr.ecr.eu-west-1.amazonaws.com

# Google Artifact Registry
gcloud auth configure-docker europe-west1-docker.pkg.dev

The permission the token needs

Being logged in is not the same as being allowed. Token scopes trip people up constantly:

A GHCR push failing with denied: permission_denied: write_package while pulls work is a token with read:packages and not write:packages.

ECR is the one with an extra trap: ecr:GetAuthorizationToken is required to log in at all, and it is a separate action from the pull permissions. An IAM policy with the pull actions but not that one fails at the login step, before it ever reaches the image.

In GitHub Actions

Use the built-in token rather than a PAT, and grant the job the scope it needs:

packages: write is the line people miss. Since GitHub tightened default token permissions, a workflow without it gets a read-only token and the push fails with denied even though the login succeeded.

A GHCR package also has its own visibility and access list, separate from the repository's. A private package in a public repo still needs an explicitly linked repository or an org-level grant.

BASHDiagnostic / remediation command
jobs:
  build:
    permissions:
      contents: read
      packages: write
    steps:
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

On Kubernetes: ErrImagePull with the same message

The kubelet has no access to your laptop's ~/.docker/config.json. It needs the credential as a Secret:

Two things that catch people out:

To avoid repeating imagePullSecrets in every manifest, attach it to the namespace's default ServiceAccount:

On EKS, GKE and AKS, pulls from the matching cloud registry usually work with no Secret at all, because the node's instance role carries the permission. If those pulls fail, fix the node role rather than adding a Secret.

BASHDiagnostic / remediation command
kubectl create secret docker-registry ghcr-creds \
  --docker-server=ghcr.io \
  --docker-username=myuser \
  --docker-password="$GITHUB_TOKEN"

A checklist

Frequently Asked Questions

Why does Docker say a repository does not exist when I know it does?

Because Docker will not confirm the existence of a private repository to an unauthenticated caller. Doing so would let anyone enumerate private image names, so a repository you cannot see and a repository that was never created return the same message. The practical consequence is that a typo and a permissions problem are indistinguishable from the error alone. Run docker manifest inspect against the reference: if it succeeds anonymously the repository is public and the name is the problem, and if it fails the same way you are looking at credentials.

What is the difference between "pull access denied" and "unauthorized: authentication required"?

pull access denied generally means no credential was presented at all, so the registry treated the request as anonymous. unauthorized: authentication required more often means a credential was presented and rejected, typically an expired token. ECR tokens last 12 hours, so a long-running build agent that logged in yesterday hits this reliably. The distinction is not perfectly consistent across registries, but it is a useful first hint about whether to check for a missing login or a stale one.

Why does docker login succeed but the push still fail?

Authentication and authorization are separate. Logging in proves who you are; pushing needs a token scope that permits writing. On GHCR a token with read:packages logs in happily and then fails the push with denied: permission_denied: write_package, because it needs write:packages. In GitHub Actions the equivalent is a job without permissions: packages: write, which receives a read-only token. On ECR, the push actions such as ecr:PutImage and ecr:UploadLayerPart are separate from the pull ones in the IAM policy.

How do I pull a private image in Kubernetes?

Create a docker-registry Secret and reference it from the Pod with imagePullSecrets, because the kubelet cannot see the Docker config on your machine. Two details cause most of the failures: the Secret is namespaced, so it must exist in the same namespace as the Pod and does not follow a manifest into a new one, and --docker-server must match the registry host exactly, which for Docker Hub means https://index.docker.io/v1/ rather than docker.io. To avoid repeating it everywhere, patch the namespace's default ServiceAccount with the pull secret.

Why do my ECR pulls stop working after a few hours?

Because aws ecr get-login-password issues a token that expires after 12 hours. Anything that logged in once and stays alive, such as a self-hosted build agent or a long-lived developer VM, starts failing when that window closes. Re-run the login as a step in each job rather than once during machine setup. Inside EKS this is usually a non-issue, because the kubelet uses the node's IAM role to fetch fresh credentials for ECR automatically and no Secret or login is involved.

Reference and practice

Learn the underlying concept

Other Docker errors

Free, comprehensive DevOps tutorials and learning roadmaps. Master Docker, Kubernetes, CI/CD, and more.

Get notified about new tutorials and features.

• Roadmaps

• Interview Prep

• Certifications

• Company

© 2026 DevOpsLesson. All rights reserved.

Need to practice this scenario?

Simulate this error condition and practice recovery commands safely in our in-browser terminal.

Open Docker Sandbox