fizz.today

TIL: docker login silently fails when credsStore is set

docker login --password-stdin exited 0. Thirty seconds later, docker compose pull returned no basic auth credentials.

The host had /root/.docker/config.json with "credsStore": "ecr-login" — installed months ago for a different workflow, forgotten about. When credsStore is set, Docker delegates credential storage to that helper unconditionally. My explicit docker login call succeeded, Docker accepted the OIDC token I’d passed from the CI runner, then handed it to ecr-login for safekeeping. The helper discarded it. It generates credentials on demand from the IAM role in ~/.aws/config — which authenticates to the legacy account’s ECR, not ramparts’. The token I’d passed was for a completely different AWS account.

The token I’d carefully piped in via --password-stdin was accepted and thrown away in the same operation. Exit code 0 the whole time.

DOCKER_CONFIG

The fix is to sidestep the helper entirely:

- name: ECR login
  shell: |
    echo "{{ ecr_token }}" | \
      docker login --username AWS --password-stdin {{ ecr_registry }}
  environment:
    DOCKER_CONFIG: /tmp/ecr-deploy

DOCKER_CONFIG redirects Docker to a clean directory with no config.json, no credsStore, no helper. The token lands in /tmp/ecr-deploy/config.json and stays put. Every subsequent command — docker compose pull, docker compose up — needs the same environment variable, or it falls back to the default config and the helper eats the credential again.

The assumption I had wrong

I treated docker login as authoritative. It isn’t. When a credential helper is configured, docker login is a suggestion. The helper is the authority. Docker accepted my credential, then deferred to a process I didn’t invoke, which decided the credential wasn’t needed.

Exit 0 meant “I processed your request,” not “your credential is stored.”

#docker #ecr #aws #til