Docker "pull access denied ... repository does not exist" in CI
Docker could not pull the image and cannot tell whether it is private or absent. pull access denied ... repository does not exist or may require docker login means the name is wrong, the repo is private and you are not authenticated, or the registry prefix is missing.
What this error means
A docker pull / docker run / base-image FROM fails with Error response from daemon: pull access denied for <image>, repository does not exist or may require docker login.
docker: Error response from daemon: pull access denied for myorg/internal-api,
repository does not exist or may require 'docker login': denied: requested access to the resource is denied.Diagnose it: separate auth from naming from rate limits
Registry errors look alike and have unrelated causes. Work out which of the three you have before changing credentials, because a malformed image reference produces an error that reads like an authentication failure.
# 1. is the reference even valid? (lowercase, no spaces, valid tag)
docker image inspect "$IMAGE" 2>&1 | head -2
# 2. are you authenticated to the right registry?
cat ~/.docker/config.json | grep -o '"[^"]*\.[^"]*"' | head
# 3. are you rate limited? (Docker Hub anonymous pulls)
curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimit-preview/test:pull" \
| grep -o '"token"' >/dev/null && echo "token ok"Common causes
The image name or registry prefix is wrong
A typo, or omitting the registry (so it defaults to Docker Hub) when the image actually lives on GHCR/ECR, makes Docker look in the wrong place - where the repo does not exist.
The repository is private and you are not logged in
For a private image, no docker login (or a token without pull scope) is reported the same way as a missing repo, for security.
The repository genuinely does not exist
A repo that was never created or pushed to returns this even with valid credentials.
How to fix it
Use the full reference and authenticate
Include the registry host and log in with pull scope for private images.
echo "$TOKEN" | docker login ghcr.io -u "$USER" --password-stdin
docker pull ghcr.io/myorg/internal-api:1.4.2Confirm the image exists where you expect
Verify the name, tag, and registry namespace before pulling.
# check the registry's package/repository list first, then:
docker pull ghcr.io/myorg/internal-api:1.4.2Authenticate in the job, not in the image
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# GHCR needs this on the job or the push is rejected as unauthorised
permissions:
contents: read
packages: writeHow to prevent it
- Always use a fully-qualified image reference including the registry host.
- Authenticate with pull scope for private base images in CI.
- Confirm the repository exists in the expected namespace.