Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Podman By James Joyner IV · · 9 min read Last reviewed Jul 2026

Podman Error: 'unauthorized: authentication required' When Pulling an Image

Quick answer

Fix Podman's 'unauthorized: authentication required' pull error: podman login, auth.json locations, REGISTRY_AUTH_FILE, --authfile, root vs rootless credentials, and registries.conf.

  • #podman
  • #containers
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this Podman error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Exact Error Message

$ podman pull registry.example.com/team/api:1.4.2
Trying to pull registry.example.com/team/api:1.4.2...
Error: initializing source docker://registry.example.com/team/api:1.4.2:
unable to retrieve auth token: invalid username/password:
unauthorized: authentication required

Under a systemd unit or sudo, the same image often fails differently:

Error: copying system image from manifest list: determining manifest MIME type
for docker://docker.io/library/redis:7: reading manifest 7 in docker.io/library/redis:
unauthorized: authentication required

What It Means

Podman does not hold a persistent daemon-side credential store the way a Docker daemon does. Every pull is performed by the client process itself, which reads a static JSON credentials file, presents a Basic auth header to the registry’s token endpoint, exchanges that for a short-lived bearer token, and then fetches the manifest. unauthorized: authentication required is the registry telling you that the token exchange either presented no credentials at all or presented ones it rejected for that specific repository.

The subtlety is which file Podman read. By default a rootless Podman looks at ${XDG_RUNTIME_DIR}/containers/auth.json — a path under /run/user/<uid>, which is tmpfs and disappears on logout. Root Podman looks at /run/containers/<uid>/auth.json instead. Podman will also fall back to reading ~/.docker/config.json if no containers auth file exists, which is why docker login sometimes appears to “fix” Podman and why the same podman pull succeeds interactively but fails the moment it runs as root, in a systemd service, or in CI.

Common Causes

  • You never ran podman login for that registry host, or logged into a different host than the image reference actually resolves to.
  • Credentials live in your user’s ${XDG_RUNTIME_DIR} auth.json but the pull is running as root via sudo or a systemd unit, which reads a different file.
  • The login was stored in ~/.docker/config.json by a docker login and the runtime user has no home directory or a different HOME.
  • XDG_RUNTIME_DIR is unset in the non-interactive environment, so Podman writes and reads credentials from an unexpected path.
  • The image is a short name and unqualified-search-registries resolved it to a registry you are not authenticated against.
  • Docker Hub anonymous pull-rate limits are being hit, and the registry answers with an auth challenge rather than a rate-limit message.

Diagnostic Commands

First confirm whether Podman believes you are logged in at all, and to which host:

podman login --get-login registry.example.com
podman login --get-login docker.io

Find out which credentials file is actually in play and inspect it. The keys are registry hostnames, and the values are base64 user:password pairs:

echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
ls -l "${XDG_RUNTIME_DIR}/containers/auth.json" ~/.docker/config.json 2>/dev/null
jq '.auths | keys' "${XDG_RUNTIME_DIR}/containers/auth.json"

Compare that with what root sees, since the two stores are entirely separate:

sudo ls -l /run/containers/0/auth.json
sudo podman login --get-login registry.example.com

Check how a short image name resolves before blaming credentials:

grep -vE '^\s*#' /etc/containers/registries.conf | grep -A5 unqualified-search
podman info --format '{{.Registries}}'

Test the registry independently with skopeo, which uses the same credential logic but removes storage and pull mechanics from the picture:

skopeo inspect docker://registry.example.com/team/api:1.4.2
skopeo inspect --authfile "${XDG_RUNTIME_DIR}/containers/auth.json" \
  docker://registry.example.com/team/api:1.4.2

Finally, turn up logging to see the exact token request Podman issued:

podman --log-level=debug pull registry.example.com/team/api:1.4.2 2>&1 | grep -iE 'auth|token|401'

Step-by-Step Resolution

  1. Log in to the exact registry host in the image reference. Note that Docker Hub’s host key is docker.io, not index.docker.io or hub.docker.com:
podman login registry.example.com
podman login docker.io -u myuser
  1. If the pull runs as root, log in as root. The rootless and root credential stores never share state:
sudo podman login registry.example.com
sudo podman pull registry.example.com/team/api:1.4.2
  1. For automation, write credentials to a stable file instead of the volatile runtime directory, and point Podman at it explicitly:
podman login --authfile /etc/containers/pull-secret.json registry.example.com
podman pull --authfile /etc/containers/pull-secret.json registry.example.com/team/api:1.4.2
  1. For a systemd unit, set REGISTRY_AUTH_FILE so every Podman invocation in that unit finds the same file. Do not rely on XDG_RUNTIME_DIR existing:
[Service]
Environment=REGISTRY_AUTH_FILE=/etc/containers/pull-secret.json
ExecStart=/usr/bin/podman run --rm registry.example.com/team/api:1.4.2
  1. Eliminate short-name ambiguity by fully qualifying the image, or by pinning the search list so api:1.4.2 cannot silently resolve to Docker Hub:
# /etc/containers/registries.conf
unqualified-search-registries = ["registry.example.com", "docker.io"]
short-name-mode = "enforcing"
  1. Verify the fix end to end and confirm the token now succeeds:
podman login --get-login registry.example.com
skopeo inspect docker://registry.example.com/team/api:1.4.2 >/dev/null && echo "auth OK"
podman pull registry.example.com/team/api:1.4.2

If a credential helper is configured (credHelpers or credsStore in ~/.docker/config.json), Podman will shell out to the matching docker-credential-* binary. Make sure that binary is on PATH for the service user too — a helper that works in your shell frequently is not present in a systemd unit’s minimal environment.

Prevention

  • Use a dedicated pull-secret file at a stable path and set REGISTRY_AUTH_FILE in every unit and CI job.
  • Always fully qualify image references in production manifests, Quadlets, and Containerfiles.
  • Give CI a read-only registry robot account rather than reusing a personal login that rotates.
  • Authenticate to Docker Hub even for public images so you get the authenticated pull-rate allowance.
  • Keep short-name-mode = "enforcing" so an ambiguous name fails loudly instead of hitting the wrong registry.
  • Remember that root and rootless are separate credential namespaces; document which one your service runs as.
  • Error: short-name "api:1.4.2" did not resolve to an alias and no unqualified-search registries are defined — a resolution problem, not authentication. See short name did not resolve.
  • toomanyrequests: You have reached your pull rate limit — Docker Hub throttling; authenticate or mirror the image.
  • x509: certificate signed by unknown authority — TLS trust for the registry CA, before any credential is even sent.
  • Error: cannot re-exec process — a rootless user-namespace failure that can mask itself as a pull failure. See cannot re-exec process.

Frequently Asked Questions

Why does podman pull work in my shell but fail in a systemd unit? Your interactive session has XDG_RUNTIME_DIR pointing at /run/user/<uid>, where podman login wrote containers/auth.json. A system unit runs as root with no such variable and reads /run/containers/0/auth.json, which is empty. Set REGISTRY_AUTH_FILE to a shared path.

Do I need to log in for public images? Not strictly, but on Docker Hub anonymous pulls are rate-limited per IP and the registry answers exhausted anonymous quota with an auth challenge. Logging in raises the limit and turns confusing unauthorized messages into clear ones.

Where should I store credentials permanently? ${XDG_RUNTIME_DIR}/containers/auth.json is tmpfs and vanishes on logout, so use podman login --authfile /path/to/file.json with a root-owned, mode 0600 file for anything long-lived.

Does docker login work for Podman? Often yes, because Podman falls back to ~/.docker/config.json when no containers auth file exists. It is a fragile coincidence though — a credsStore entry or a different HOME breaks it. For more container fixes, see the Podman guides.

Free download · 368-page PDF

Fixed it? Get 500 Podman & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.