KhueApps
Home/DevOps/Fix 'insufficient_scope: authorization failed' with Docker private registries

Fix 'insufficient_scope: authorization failed' with Docker private registries

Last updated: October 07, 2025

What this error means

You tried to pull or push an image and the registry’s token service refused your request. The token you presented is valid, but it lacks the required scope (permissions) for the repository or action (pull, push).

Typical message:

  • insufficient_scope: authorization failed

Root causes:

  • Not logged in to the correct registry host
  • Token lacks repository scope (e.g., pull or push)
  • Wrong repository path/namespace
  • Repository doesn’t exist or is private and you lack access
  • Cloud registry login flow not used (ECR, GHCR, ACR, GCR)

Quickstart: the fast fixes

  1. Ensure you’re logging into the exact registry host:
# Docker Hub
docker login docker.io

# GitHub Container Registry
docker login ghcr.io

# AWS ECR (example region us-east-1)
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com

# Azure Container Registry
az acr login --name acmeacr
  1. Use a token with the right scopes:
  • Docker Hub: Personal Access Token for CLI, repository access granted
  • GHCR: PAT with read:packages (pull) and write:packages (push), SSO enabled if org requires it
  • ECR: IAM permissions ecr:GetAuthorizationToken, ecr:BatchGetImage, ecr:PutImage, etc.
  • ACR: Role assignment (AcrPull/AcrPush)
  1. Reference the correct repository name and tag:
docker pull ghcr.io/acmeinc/web:1.0.0

If the above still fails, follow the steps below.

Minimal working example (Docker Hub private repo)

Scenario: Push a private image to docker.io/acmeinc/private-app:1.0.0

# 1) Authenticate with a Docker Hub PAT (not your account password)
docker login docker.io -u acmeci  # will prompt for PAT

# 2) Build locally
docker build -t docker.io/acmeinc/private-app:1.0.0 .

# 3) Push
docker push docker.io/acmeinc/private-app:1.0.0

# 4) Pull (from another machine or after logout)
docker logout docker.io

docker login docker.io -u acmeci  # PAT again

docker pull docker.io/acmeinc/private-app:1.0.0

If you see insufficient_scope at step 3 or 4, your token lacks push or pull scope for acmeinc/private-app, or you’re not hitting the right registry host.

Step-by-step troubleshooting

  1. Confirm the image reference
  • Use the fully qualified name: registry/namespace/name:tag
  • Examples:
    • docker.io/acmeinc/private-app:1.0.0
    • ghcr.io/acmeinc/web:1.0.0
    • 123456789012.dkr.ecr.us-east-1.amazonaws.com/api:latest
  1. Ensure repository exists and you have access
  • For new repos, push once to create them (some registries auto-create on push; others require creating beforehand).
  1. Re-login to the correct host
docker logout ghcr.io || true
docker login ghcr.io
  1. Inspect the auth challenge (optional but precise)
# Ask for a manifest; registry responds 401 with WWW-Authenticate
curl -sI -H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
  https://registry-1.docker.io/v2/acmeinc/private-app/manifests/1.0.0

Look for WWW-Authenticate: Bearer realm=..., service=..., scope=repository:acmeinc/private-app:pull. If your token lacks that scope (pull) or push when pushing, you’ll get insufficient_scope.

  1. Use the registry-native login flow
  • Docker Hub: PAT only for CLI
  • GHCR: docker login ghcr.io with PAT that has read:packages/write:packages and is SSO-enabled for orgs
  • ECR: use aws ecr get-login-password; ensure region matches the repo
  • ACR: az acr login or docker login with admin user or service principal
  1. Verify Docker credential store
cat ~/.docker/config.json
  • Check auths and credHelpers entries for your registry host. Misconfigured credential helpers can cause stale or missing tokens.
  1. Retry with a fresh token
  • Rotate PATs or re-run cloud CLI login to refresh short-lived tokens (ECR tokens expire after 12 hours).
  1. Test a pull-only operation
docker pull ghcr.io/acmeinc/web:1.0.0

If pull works but push fails, you’re missing push scope/role.

Registry-specific quick reference

RegistryLogin commandRequired permissions for pull/push
Docker Hubdocker login docker.ioRepo access; use a Personal Access Token for CLI
GHCRdocker login ghcr.ioread:packages (pull), write:packages (push), org SSO enabled
AWS ECRaws ecr get-login-passwordIAM: ecr:GetAuthorizationToken, ecr:BatchGetImage, ecr:PutImage, ecr:InitiateLayerUpload
Azure ACRaz acr login --name NAMEACR roles: AcrPull, AcrPush
GCR/Artifact Registrygcloud auth configure-dockerIAM roles: Artifact Registry Reader/Writer (or GCR equivalents)

CI examples

GitHub Actions pulling from GHCR:

name: ci
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Build and push
        run: |
          docker build -t ghcr.io/acmeinc/web:${{ github.sha }} .
          docker push ghcr.io/acmeinc/web:${{ github.sha }}

ECR in a CI runner (region us-east-1):

aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com

docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/api:latest

Common pitfalls

  • Using account password instead of a PAT where required
  • Logging into the wrong host (docker.io vs ghcr.io vs ECR hostname)
  • Registry SSO not authorized for your token (GHCR orgs)
  • ECR region mismatch or token expired
  • Wrong repository path (missing org/namespace)
  • Assuming anonymous pull for a private repo
  • Stale credentials in credential helper; fix by re-login or clearing entries

Performance and reliability notes

  • Avoid logging in for every build step. Log in once per job and reuse Docker’s credential store.
  • Use registry-native credential helpers (gcloud, ECR, ACR) to obtain short-lived tokens automatically.
  • Prefer fully qualified image names to prevent accidental pulls from the wrong registry.
  • Minimize permission scope: pull-only for deployment nodes; push only in build jobs.
  • Cache base images in your registry to reduce cross-registry pulls and auth round-trips.

Tiny FAQ

  • What causes insufficient_scope?

    • Your token is valid but lacks the repository scope (pull or push) requested by the registry.
  • How do I see what scope is required?

    • Send a manifest request with curl and inspect the WWW-Authenticate header. It contains the scope and service the registry expects.
  • Why does pull work but push fails?

    • Your token has pull only. Ensure write:packages (GHCR), AcrPush (ACR), or ecr:PutImage (ECR) permissions.
  • Do I need to log in for private base images in Dockerfiles?

    • Yes. Ensure the builder has credentials before the FROM step (BuildKit build secrets/registry auth).
  • How do I reset bad credentials?

    • Run docker logout <registry> or remove the entry from ~/.docker/config.json, then log in again.

Series: Docker

DevOps