Docker: "Pull Access Denied" — Registry Auth, Names, and Rate Limits

The registry refused the pull: not authenticated, the image name is wrong (the classic missing namespace), or the Hub rate limit hit. The error text names which of the three.

What you'll see

Root causes

Image name wrong: missing namespace/tag

docker pull myapp looks for library/myapp on Docker Hub — YOUR image is username/myapp. The error says access denied, but nothing about it was private. docker images on the build machine shows the real name.

Private registry without credentials (or expired ones)

docker login needed for that registry on that machine/CI runner. CI tokens rotate; expired ones produce the same message as never-logged-in.

Docker Hub rate limits

Anonymous pulls are capped per IP (shared CI NATs hit it fast). toomanyrequests / 429 in the error is the tell — pulls that 'should work' failing intermittently.

Fix it

  1. Verify the image name including namespace
    docker pull <user>/<image>:<tag>   # exact, never the namespace-less shortcut
  2. Authenticate for private images
    echo "$TOKEN" | docker login -u <user> --password-stdin <registry>   # stdin keeps it out of the process list
  3. CI: check the runner's auth state
    # GH Actions: use docker/login-action with the right registry; expired PAT = silent 401s
  4. Rate limits: mirror or authenticate (authenticated pulls get a higher cap)
    # docker login (free account raises the cap) ; or configure the runner to pull from a mirror you control

Field note

push access denied vs pull access denied overlap in wording: pushes fail with auth/permissions on the repo, pulls fail with name/auth/rate-limit. Read which verb the daemon reported before debugging credentials. Images built locally under a short name exist only locally: pushing tags them with the full path, and pulling requires using it.

Common questions

Why does docker pull myapp fail but docker run myapp works locally?

The local image satisfies run without a pull. Any context forcing a real pull (new machine, --pull flag, compose with pull policy) reveals the name/auth problem that was hidden all along.

How do I check if I'm rate limited?

Pull any public image and read the error: toomanyrequests means capped. token.docker.dev shows your current tier; authenticated pulls raise the ceiling immediately.

Ship it right the first time

A production-shaped compose stack: healthchecks, resource limits, log rotation. Never debug a boot race again.

Docker Production Starter — $19 →

One-time. Yours to modify. Instant download from the NinjaOps template store.

Get new fixes by email

One short email when new fixes and production templates drop. No spam, unsubscribe anytime.

Partner pick — sponsored

Vultr — our lab-environment pick for this stack

Spin up a cloud server in 60 seconds and reproduce this fix yourself — pay by the hour.

Get Vultr →
Also vetted

Sentry — Free tier: see the exact line of code that broke, before users report it.

Get Sentry →

We earn a commission if you buy through our links — it never costs you extra. More vetted tools on our picks hub · comparing clouds? DigitalOcean vs Vultr and vs AWS · full deals: DigitalOcean · Vultr · NordLayer · Semrush