KhueApps
Home/DevOps/How to fix 'no basic auth credentials' when pushing to a registry

How to fix 'no basic auth credentials' when pushing to a registry

Last updated: October 07, 2025

Overview

The error "no basic auth credentials" means the Docker client does not have usable credentials for the registry you’re pushing to. Common causes:

  • You didn’t run docker login for that registry.
  • The image is tagged for a different registry than you expect.
  • The registry hostname/port in your tag doesn’t match what’s in ~/.docker/config.json.
  • A credential helper or creds store is misconfigured or inaccessible (especially in CI).

This guide shows quick fixes for Docker Hub, GHCR, ECR, GCR/AR, ACR, and private registries, plus CI examples.

Quickstart (most cases)

  1. Identify the exact registry host you’re pushing to.
  2. Login to that host with docker login (use --password-stdin in CI).
  3. Ensure the image tag includes the same registry host.
  4. Push again.

Minimal working example

# Example: push busybox to Docker Hub under user/repo:1.0
export DOCKERHUB_USER="youruser"
export DOCKERHUB_TOKEN="your-token-or-password"
export IMAGE="${DOCKERHUB_USER}/demo:1.0"

# 1) Login (writes credentials to ~/.docker/config.json)
printf "%s" "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USER" --password-stdin docker.io

# 2) Tag with the exact registry host (docker.io implied, but explicit is safest)
docker pull busybox:latest
docker tag busybox:latest docker.io/$IMAGE

# 3) Push
docker push docker.io/$IMAGE

How Docker decides credentials

Docker reads credentials from ~/.docker/config.json or the path in $DOCKER_CONFIG. Entries can be:

  • auths: base64-encoded credentials per registry.
  • credHelpers: per-registry credential helper (e.g., ghcr.io -> "ghcr").
  • credsStore: default OS-wide credential helper (osxkeychain, wincred, secretservice, pass).

Example config.json snippet:

{
  "auths": {
    "https://index.docker.io/v1/": {},
    "ghcr.io": {}
  },
  "credHelpers": {
    "ghcr.io": "ghcr"
  },
  "credsStore": "osxkeychain"
}

If the registry in your tag doesn’t match a key here, Docker can’t fetch credentials and you’ll see the error.

Step-by-step fix

  1. Check your tag is fully qualified.

    • If you omit a registry, Docker assumes Docker Hub (docker.io). Example: repo:tag => docker.io/library/repo:tag (or user/repo:tag when you own it).
    • To push elsewhere, tag with the full host (e.g., ghcr.io/USER/repo:tag, 123456789012.dkr.ecr.us-east-1.amazonaws.com/repo:tag).
  2. Login to the same host you’re tagging.

    • docker login <registry>. Use --password-stdin for security.
  3. Verify config path and contents.

    • echo ${DOCKER_CONFIG:-$HOME/.docker}
    • cat ${DOCKER_CONFIG:-$HOME/.docker}/config.json
    • Ensure an auths/credHelpers entry exists for your registry host (exact hostname and port).
  4. Re-tag and push.

    • docker tag local:tag <registry>/<namespace>/<repo>:<tag>
    • docker push <same-exact-reference>
  5. If still failing, clear and re-authenticate.

    • docker logout <registry>
    • Remove stale entries from config.json and login again.

Registry-specific commands

  • Docker Hub
printf "%s" "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USER" --password-stdin docker.io
  • GitHub Container Registry (GHCR)
# PAT must have: read:packages, write:packages (and delete:packages if needed)
printf "%s" "$GHCR_PAT" | docker login ghcr.io -u "$GITHUB_USER" --password-stdin
  • Amazon ECR
aws ecr get-login-password --region us-east-1 \
  | docker login --username AWS --password-stdin \
  123456789012.dkr.ecr.us-east-1.amazonaws.com
  • Google Artifact Registry / GCR
# Artifact Registry example (preferred over GCR)
gcloud auth print-access-token \
  | docker login -u oauth2accesstoken --password-stdin \
  us-central1-docker.pkg.dev

# GCR example
gcloud auth print-access-token \
  | docker login -u oauth2accesstoken --password-stdin \
  gcr.io
  • Azure Container Registry (ACR)
# If admin user enabled (for quick tests)
printf "%s" "$ACR_PASSWORD" | docker login "$ACR_NAME".azurecr.io -u "$ACR_USERNAME" --password-stdin

# Or via Azure CLI (tokens/managed identities preferred)
az acr login --name "$ACR_NAME"
  • Private/self-hosted registry
# Use the exact host:port you configured (default 5000)
printf "%s" "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin registry.example.com:5000

CI/CD examples

  • GitHub Actions
jobs:
  push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_PAT }}
      - run: |
          docker buildx build -t ghcr.io/${{ github.repository_owner }}/app:latest --push .
  • GitLab CI
build:
  image: docker:24
  services: [docker:24-dind]
  variables:
    DOCKER_TLS_CERTDIR: ""
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
    - docker build -t "$CI_REGISTRY_IMAGE:latest" .
    - docker push "$CI_REGISTRY_IMAGE:latest"
  • Generic shell runner
export DOCKER_CONFIG="$PWD/.docker"
mkdir -p "$DOCKER_CONFIG"
printf "%s" "$PASSWORD" | docker login -u "$USERNAME" --password-stdin registry.example.com
# Ensure tags match the registry

Common pitfalls

  • Wrong registry in tag: pushing myorg/app:latest defaults to Docker Hub, not GHCR/ECR.
  • Missing namespace: some registries require user/org prefix (e.g., ghcr.io/USER/app).
  • Credential helper mismatch: credHelpers/credsStore set but helper binary not present in CI.
  • Running docker with sudo uses root’s config at /root/.docker, not your user’s. Either avoid sudo or set DOCKER_CONFIG.
  • PAT scope issues (GHCR needs write:packages; GCP uses oauth2accesstoken; ECR uses AWS-generated token).
  • Multiple accounts: credentials for the host exist but belong to a different user/org without push rights.
  • Stale or corrupted config.json: remove offending entries and re-login.

Performance notes

  • Use BuildKit/buildx with --push to stream layers as they are built, reducing round-trips:
docker buildx build --push -t ghcr.io/USER/app:latest .
  • Minimize layer churn to avoid re-pushing large layers; keep Dockerfile layers stable.
  • Use .dockerignore to shrink context size; smaller contexts mean fewer/lighter layers to upload.
  • Enable cache export/import to reuse layers across CI runs:
docker buildx build \
  --cache-to=type=registry,ref=ghcr.io/USER/app:cache,mode=max \
  --cache-from=type=registry,ref=ghcr.io/USER/app:cache \
  --push -t ghcr.io/USER/app:latest .
  • Push only necessary tags (avoid pushing both latest and a large set of unique tags unless needed).

Troubleshooting checklist

  • docker info | grep Username shows your Docker Hub login when applicable.
  • docker logout <registry> then login again with the correct host.
  • Confirm the image reference you push matches the login host byte-for-byte.
  • Inspect ~/.docker/config.json for matching auths/credHelpers entries.
  • In CI, print echo "$DOCKER_CONFIG" and ls "$DOCKER_CONFIG" to ensure the right path is used.

FAQ

  • Why does it work locally but not in CI? Your local machine uses an OS credential helper. In CI, that helper may not exist. Use docker login with --password-stdin and DOCKER_CONFIG set to a writable path.

  • Do I need to base64-encode credentials manually? No. docker login writes the proper format or stores credentials via helpers. Don’t edit auth strings by hand.

  • Can I avoid storing plaintext passwords? Yes. Use tokens/PATs (GHCR, ACR), aws ecr get-login-password (ECR), or gcloud access tokens (GCR/AR). Prefer short-lived tokens in CI.

  • Why do I still get auth errors after login? Usually the tag registry doesn’t match the login host, or your account lacks push permission to the repo/namespace.

Series: Docker

DevOps