Docker Credential Helper Tutorial#
This tutorial shows how to use scafctl as a Docker/Podman credential helper, exposing scafctl’s AES-256-GCM encrypted credential store to the container ecosystem.
Prerequisites#
- scafctl installed and available in your PATH
- Docker, Podman, or Buildah installed
- Familiarity with Authentication Tutorial
Table of Contents#
- Overview
- Quick Setup
- Manual Setup
- Per-Registry Configuration
- How It Works
- Using with Podman/Buildah
- Verification
- Composing with catalog login
- Uninstall
- Troubleshooting
Overview#
The Docker credential helper protocol allows external programs to manage registry credentials for Docker, Podman, and Buildah. By using scafctl as a credential helper, you get:
- Encrypted-at-rest credentials – scafctl stores all secrets using AES-256-GCM encryption
- Single credential store – credentials stored via
scafctl catalog loginare automatically available to Docker - No plaintext files – unlike Docker’s default
config.jsonauth storage
Quick Setup#
Install scafctl as the credential helper for Docker:
scafctl credential-helper install --dockerThis creates a docker-credential-scafctl symlink in ~/.local/bin and updates ~/.docker/config.json to use scafctl as the credential store.
For Podman:
scafctl credential-helper install --podmanManual Setup#
1. Create the Symlink#
scafctl credential-helper installThis creates ~/.local/bin/docker-credential-scafctl pointing to your scafctl binary. Ensure ~/.local/bin is on your PATH:
export PATH="$HOME/.local/bin:$PATH"2. Configure Docker#
Add to ~/.docker/config.json:
{
"credsStore": "scafctl"
}This tells Docker to use scafctl for all registry credential lookups.
Per-Registry Configuration#
Instead of making scafctl the global credential store, you can configure it for specific registries only:
scafctl credential-helper install --docker --registry ghcr.ioThis adds a credHelpers entry to Docker’s config:
{
"credHelpers": {
"ghcr.io": "scafctl"
}
}You can add multiple registries:
scafctl credential-helper install --docker --registry ghcr.io
scafctl credential-helper install --docker --registry quay.ioHow It Works#
The credential helper protocol uses four stdin/stdout commands:
| Command | Input | Output | Description |
|---|---|---|---|
get | Server URL (text) | JSON credential | Retrieve credentials |
store | JSON credential | – | Store credentials |
erase | Server URL (text) | – | Remove credentials |
list | – | JSON map | List all credentials |
Credential Format#
{
"ServerURL": "https://ghcr.io",
"Username": "oauth2",
"Secret": "gho_xxxxxxxxxxxx"
}Error Format#
{
"message": "credentials not found"
}Lookup Order#
When Docker calls get, scafctl checks credentials in this order:
- Dynamic token resolution – infers the auth handler for the registry and mints a fresh token from it (auto-refreshing via a stored refresh token when available)
- credhelper namespace – credentials stored via
docker login(through scafctl) - Native credential store – credentials stored via
scafctl catalog login
This means credentials from any source are available to Docker.
Note:
getruns as a non-interactive subprocess (no TTY). If the inferred auth handler has no valid token and can only refresh through an interactive login, dynamic resolution cannot complete inside the helper. scafctl emits an actionable error naming the exact command to run, for example:{ "message": "no valid credentials for https://ghcr.io: run 'scafctl auth login github' to re-authenticate (interactive login cannot run inside a credential-helper subprocess)" }Run the named
auth loginonce; subsequentgetcalls then refresh silently.
Using with Podman/Buildah#
Podman and Buildah use the same credential helper protocol:
scafctl credential-helper install --podmanOr manually configure ~/.config/containers/auth.json:
{
"credHelpers": {
"ghcr.io": "scafctl"
}
}Verification#
Test the credential helper directly:
# List all stored credentials
docker-credential-scafctl list
# Get credentials for a specific registry
echo "https://ghcr.io" | docker-credential-scafctl get
# Verify Docker can use it
docker pull ghcr.io/your-org/your-image:latestComposing with catalog login#
Credentials stored via scafctl catalog login are automatically available through the credential helper:
# Login via scafctl
scafctl catalog login ghcr.io --username oauth2 --password @- < token.txt
# Docker can now pull from ghcr.io without separate docker login
docker pull ghcr.io/your-org/your-image:latestEmbedding scafctl in another CLI#
scafctl is consumable as a library by downstream CLIs that ship their own
binary name. The credential helper works for embedders with no extra wiring:
when the binary is invoked under a docker-credential-<name> alias, the shared
root command automatically routes the helper verb into the credential-helper
command tree.
If you build your CLI via the shared scafctl.Root(...) constructor, you get
this for free – credential-helper install creates a
docker-credential-<yourbinary> symlink, and Docker/Podman invoking it just
works.
For a fully custom main that does not use scafctl.Root, call the exported,
side-effect-free dispatch helper at the very top of main:
import "github.com/oakwood-commons/scafctl/pkg/credentialhelper"
func main() {
// Route docker-credential-<name> <verb> into "credential-helper <verb>".
if rewritten, isAlias := credentialhelper.RewriteAliasArgs(os.Args); isAlias {
rootCmd.SetArgs(rewritten[1:]) // SetArgs excludes the program name
}
// ... normal command execution ...
}The actionable get error (see How It Works
) is derived from
your configured binary name, so it names your-binary auth login <handler>.
Uninstall#
# Remove symlink and Docker config entry
scafctl credential-helper uninstall --docker
# Or just the symlink
scafctl credential-helper uninstall
# For Podman
scafctl credential-helper uninstall --podmanTroubleshooting#
“docker-credential-scafctl: command not found”#
Ensure ~/.local/bin (or your custom --bin-dir) is on your PATH:
export PATH="$HOME/.local/bin:$PATH"“credentials not found”#
- Verify the credential exists:
docker-credential-scafctl list - Check if you logged in with scafctl:
scafctl catalog login <registry> - Check the server URL matches exactly (including scheme)
Master key issues#
The credential helper uses scafctl’s encrypted secrets store. If the master key is not available, all operations will fail. Ensure:
# Check if the secrets store is accessible
scafctl secrets listPermission denied on symlink#
If you can’t write to ~/.local/bin, specify a different directory:
scafctl credential-helper install --bin-dir /usr/local/bin --docker