Private Registry Authentication: Credential Helpers Instead of Plaintext Passwords
AI generated
FROM
RUN
Docker · Registry · Security
Private Registry Authentication
Credential helpers instead of plaintext passwords

A plain docker login stores credentials by default in plaintext in ~/.docker/config.json, readable by any process with file access. Credential helpers solve this by delegating passwords to a secure keystore, both locally and in CI pipelines.

16 min read Docker Registry Security

1. How docker login stores credentials by default

After a docker login against a private registry, Docker stores the credentials in the file ~/.docker/config.json, in a section called auths, organized by registry hostname. Without further configuration, the username and password are by default merely base64-encoded there, which at first glance looks like encryption but is in fact trivially reversible and offers no cryptographic protection whatsoever. Any process and any user with read access to this file can decode the credentials within seconds.

This becomes especially critical on shared development machines, CI runners with multiple parallel jobs, or backup systems that back up a user's home directory, since config.json can then potentially end up in backups, snapshots, or be viewed by other processes on the same system. On a personal laptop the risk may be manageable, but in a production CI/CD environment with many contributors and automated processes, base64 encoding as the sole protection mechanism for registry credentials is clearly inadequate.

2. How credential helpers solve the problem

A credential helper is a standalone executable program that instructs Docker to stop storing credentials directly in config.json and instead delegate them to a secure, OS-native or cloud-specific store, such as the macOS Keychain, the Windows Credential Manager, the Linux Secret Service via libsecret, or cloud-specific mechanisms like IAM roles on AWS. Docker communicates with the helper program via a simple, standardized stdin/stdout protocol that implements commands such as store, get, and erase.

A credential helper is configured via the credsStore or credHelpers key in ~/.docker/config.json. credsStore sets a single global helper for all registries, while credHelpers allows a fine-grained mapping of a helper per registry hostname, which is useful, for example, when an AWS ECR registry authenticated via IAM credentials and a Docker Hub login via the local OS keystore need to coexist.


# The credential helper binary must be on the PATH, e.g. docker-credential-osxkeychain
which docker-credential-osxkeychain

# Configure ~/.docker/config.json manually or via docker login
cat ~/.docker/config.json
# {
#   "credsStore": "osxkeychain"
# }

3. Setting up cloud-specific credential helpers

Dedicated credential helpers exist for the major cloud registries that can additionally issue temporary, automatically rotating access tokens instead of a permanent password. For AWS ECR that is docker-credential-ecr-login, which uses the existing AWS configuration, such as an IAM role or a profile from ~/.aws/credentials, to automatically request a short-lived authentication token on every docker pull or docker push, without a static password ever ending up in any configuration file.

Similar helpers exist for Google Artifact Registry with docker-credential-gcr and for Azure Container Registry with docker-credential-acr, each tightly integrated with the cloud provider's own identity and access management. The big advantage over a classic docker login with a long-lived password is that the actual credentials never sit permanently on disk and, if a machine is compromised, automatically expire on the next token refresh instead of requiring manual rotation.


# Install and configure the ECR credential helper
brew install docker-credential-ecr-login   # or via your distribution's package manager

cat ~/.docker/config.json
# {
#   "credHelpers": {
#     "123456789012.dkr.ecr.eu-central-1.amazonaws.com": "ecr-login"
#   }
# }

# No docker login needed, IAM role or profile authenticates automatically
docker pull 123456789012.dkr.ecr.eu-central-1.amazonaws.com/mironsoft/app:latest

4. Credential helpers on Linux without a desktop environment

On Linux servers and CI runners without a graphical desktop environment, the convenient libsecret-based helper is often unavailable, since it relies on a running secret service daemon such as GNOME Keyring. An alternative is docker-credential-pass, built on the established Unix tool pass, which stores credentials GPG-encrypted in a simple filesystem tree and therefore also works on headless servers without a graphical interface.

Setup requires an initialized GPG key and an initialized pass store, after which docker-credential-pass behaves like any other credential helper and is activated via credsStore in config.json. It is important that the GPG key itself in turn be kept safe, for example in a hardware token or a dedicated secrets management system, since it ultimately forms the root protection for all stored registry credentials.


# Setting up docker-credential-pass on Linux
gpg --gen-key
pass init "docker-credential-key-id"

echo '{"credsStore": "pass"}' > ~/.docker/config.json
docker login registry.mironsoft.internal

5. Passing secrets safely to docker login in CI environments

In CI pipelines, an interactive docker login with a password prompt is not practical, which is why credentials are usually passed via --password-stdin, avoiding the password appearing as a plaintext argument in the process listing or in shell history. A common mistake is using the deprecated --password flag with a direct value, since this can potentially end up in job logs, the runner's bash history file, or be visible to other processes on the same system via ps aux.

CI-native secret mechanisms such as GitHub Actions Secrets or GitLab CI/CD variables ensure the value itself appears masked in logs and does not need to be stored in plaintext in the workflow or pipeline file. Combined with --password-stdin, the password is thus never visible as a command-line argument but passed to docker login exclusively via a secure pipe.


# Secure login in CI via stdin instead of a command-line argument
echo "$REGISTRY_PASSWORD" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin

# Never do this (password ends up in process listing and history):
# docker login ghcr.io -u user --password "$REGISTRY_PASSWORD"

6. Credential helpers in Compose and Kubernetes environments

docker compose uses the same ~/.docker/config.json as the Docker CLI when pulling images, so a once-configured credential helper automatically applies to compose pull and compose build with private base images too, without separate configuration. In Kubernetes environments, however, no credential helper in the strict sense is used; instead, imagePullSecrets store credentials as a Kubernetes secret of type kubernetes.io/dockerconfigjson in the cluster and are attached to a pod or service account.

In managed Kubernetes offerings like EKS, GKE, or AKS, however, node-side credential providers, such as the ECR credential provider for EKS, frequently take on tasks similar to a local credential helper: they automatically supply short-lived tokens for the respective cloud registry, so no static imagePullSecret needs to be maintained and credentials rotate automatically at regular intervals, analogous to the local ecr-login helper.

7. The least-privilege principle for registry credentials

Regardless of the specific storage mechanism, registry credentials are subject to the same least-privilege principle as any other form of credentials: a CI job that only needs to pull images should never be equipped with a token that also has push or even delete rights. Many registries allow the creation of fine-grained access tokens or service accounts scoped to individual repositories and operations, instead of a generic admin access shared by all automated processes.

Especially in shared CI environments with many projects, it is advisable to issue narrowly scoped credentials per project or even per pipeline stage, so that a compromised token in the worst case only affects a single repository and does not endanger the entire registry infrastructure. This segmentation increases the initial configuration effort but significantly reduces the potential damage in the event of a security incident.

8. Token rotation and audit capability

Static, long-lived passwords, as arise from a classic docker login without a credential helper, must be rotated manually, which in practice is often neglected once an access works. Cloud credential helpers such as docker-credential-ecr-login structurally avoid this problem by issuing only short-lived tokens in the first place, typically valid for a few hours, so a stolen token automatically becomes useless once it expires, without any human intervention needed.

In addition, many registries offer detailed audit logs that record every pull and push together with the access token used, which, combined with fine-grained credentials issued per project, allows precise traceability of which service or pipeline accessed which image and when. With a single, shared admin access for all systems, this traceability is largely lost, since individual actions can no longer be clearly attributed to a source.

9. Best practices and a comparison of methods

As a practical ground rule: on developer laptops, an OS-native credential helper such as osxkeychain, wincred, or pass should always be enabled, never plain base64 storage in config.json. In CI pipelines, cloud-native credential providers with short-lived tokens should be preferred over static passwords wherever available, and where that is not possible, at least --password-stdin combined with masked CI secrets should be used.

The table below compares the presented mechanisms in terms of security, place of use, and maintenance effort, to make the right choice easier for your own environment.

Mechanism Storage Place of use Rotation
docker login without helper Base64 in config.json, no protection Not recommended Manual
OS credential helper (osxkeychain, wincred, pass) Encrypted OS keystore Developer laptops Manual
Cloud credential helper (ecr-login, gcr, acr) No permanent password, token on demand CI pipelines, cloud environments Automatic, short-lived
--password-stdin with CI secrets Masked CI secret, no plaintext in logs CI pipelines without a cloud helper Manual via CI secret rotation

Mironsoft

Container infrastructure, CI pipelines and deployment automation

Docker setups that hold up across the team and in production?

We review existing Dockerfiles and Compose stacks for security gaps, bloated images and fragile build pipelines, then build a container infrastructure that builds fast, runs securely and stays understandable across the team.

Dockerfile Review

Systematically optimizing multi-stage builds, layer caching and image size.

Security Audit

Hardening container isolation, secrets handling and image scanning against real attack surfaces.

CI/CD Integration

Building build pipelines, registries and deployment strategies for reproducible releases.

10. Summary

Registry Authentication: Key Takeaways

Core problem

Without a helper, docker login stores credentials only base64-encoded, not encrypted.

Credential helpers

Delegate credentials to an OS keystore or cloud IAM instead of storing them in config.json.

Cloud advantage

ecr-login, gcr, and acr issue short-lived tokens instead of static passwords.

CI practice

--password-stdin with masked CI secrets, never --password in plaintext.

11. FAQ: Registry Authentication: Key Takeaways

1Are credentials in ~/.docker/config.json encrypted?
Without a credential helper, they are stored only base64-encoded without cryptographic protection, which is trivially reversible. Only an enabled credential helper moves storage into a genuine, encrypted keystore.
2What is the difference between credsStore and credHelpers?
credsStore sets a single helper for all registries, while credHelpers allows an individual mapping of a helper per registry hostname, useful for example when authenticating against several different cloud registries at once.
3Do I still need a manual docker login for AWS ECR?
No, with docker-credential-ecr-login, Docker authenticates automatically via the existing AWS IAM configuration and requests a short-lived token as needed, without a manual docker login being necessary.
4How do I set up a credential helper on Linux without a desktop?
With docker-credential-pass, built on the GPG-based pass tool, which does not require a running desktop secret service. It requires an initialized GPG key and pass store.
5Why should I use --password-stdin instead of --password?
Because --password passes the value as a visible command-line argument that can show up in process listings, shell history, and sometimes CI logs. --password-stdin passes the password exclusively via a secure pipe.
6How do imagePullSecrets work in Kubernetes?
They store registry credentials as a Kubernetes secret of type kubernetes.io/dockerconfigjson in the cluster and are attached to a pod or service account, so the kubelet can pull private images.
7What does least privilege mean for registry credentials?
An access token should only have the minimally necessary rights, for example pull only instead of pull and push, and should be scoped to individual repositories rather than the entire registry wherever possible.
8How often should registry passwords be rotated?
Static passwords should be rotated manually on a regular basis, for example every few months. Cloud credential helpers with short-lived tokens solve this problem automatically, since tokens expire after hours anyway.
9Does docker compose also use credential helpers?
Yes, docker compose reads the same ~/.docker/config.json as the Docker CLI when pulling images, so a once-configured credential helper automatically applies to compose pull and compose build as well.
10What happens if a cloud credential helper token is compromised?
Since the tokens are typically valid for only a few hours, a stolen token automatically becomes useless once it expires, without requiring manual revocation. Audit logs additionally allow tracing any misuse.