How to decode a pull secret
- Paste one of these into the input:
kubectl get secret regcred -o yamloutput- just the Base64 value of
.dockerconfigjson - the JSON itself, for example your
~/.docker/config.json
- Registry credentials are listed first: registry, username, password, and the
authvalue split into user and password. - To create a Secret instead, open Build pull secret and fill in Registry, Username, Password and optionally Email. The result is the same Secret that
kubectl create secret docker-registrywrites.
The double Base64 inside a pull secret
A pull secret wraps Base64 inside Base64, which is why decoding it by hand takes two steps.
apiVersion: v1
kind: Secret
metadata:
name: regcred
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6ImNpLWJvdCIsInBhc3N3b3JkIjoicHVsbC1vbmx5LXRva2VuIiwiYXV0aCI6Ilkya3RZbTkwT25CMWJHd3RiMjVzZVMxMGIydGxiZz09In19fQ==
Layer one, the Secret value, decodes to the Docker config JSON:
{"auths":{"registry.example.com":{"username":"ci-bot","password":"pull-only-token","auth":"Y2ktYm90OnB1bGwtb25seS10b2tlbg=="}}}
Layer two, auth, decodes to ci-bot:pull-only-token. A .dockerconfigjson value created by kubectl create secret docker-registry starts with eyJhdXRocyI6, the Base64 of {"auths":, which makes such strings easy to spot in logs and manifests.
Formats you may run into
| Shape | Where it comes from |
|---|---|
{"auths": {"host": {...}}} | .dockerconfigjson, current ~/.docker/config.json |
{"host": {"auth": "...", "email": "..."}} | legacy .dockercfg Secrets (kubernetes.io/dockercfg) |
"https://index.docker.io/v1/" as the host | Docker Hub |
identitytoken instead of a password | registries that hand out OAuth refresh tokens after docker login |
Both Secret types are decoded here. A legacy .dockercfg has no auths wrapper, so a script written for one format silently finds nothing in the other.
Decoding in the shell
kubectl get secret regcred -o jsonpath='{.data.\.dockerconfigjson}' | base64 --decode | jq .
kubectl get secret regcred -o jsonpath='{.data.\.dockerconfigjson}' | base64 --decode \
| jq -r '.auths | to_entries[] | "\(.key) \(.value.auth | @base64d)"'
The second command prints one registry user:password line per registry. The auth value has the same format as a Basic Auth header, so the Basic Auth decoder reads it too.
Why a pull secret fails
- Wrong registry key. The host in
authsmust match the image reference:ghcr.io/org/appneedsghcr.io, and Docker Hub images needhttps://index.docker.io/v1/. - Secret in another namespace.
imagePullSecretsonly finds Secrets in the Pod’s own namespace. - Expired token. Some registries issue short-lived passwords; Amazon ECR tokens, for example, are valid for 12 hours. A Secret that worked yesterday then gives
ImagePullBackOfftoday. - Hand-built JSON. A password copied with a trailing newline or space, or an
authvalue encoded from the wrong string, fails silently. Compare the decodedauthwith the username and password fields, or recreate the Secret withkubectl create secret docker-registry.
Anyone who can read the Secret can use the account, since Base64 hides nothing. Give pull secrets read-only registry tokens.
FAQ
How do I decode an imagePullSecret?
Paste the output of kubectl get secret regcred -o yaml above. In a shell, kubectl get secret regcred -o jsonpath='{.data.\.dockerconfigjson}' | base64 --decode prints the JSON, and its auth field is Base64 once more.
What is the auth field in config.json?
It is username:password in Base64, the same format as an HTTP Basic Authorization header. Y2ktYm90OnB1bGwtb25seS10b2tlbg== decodes to ci-bot:pull-only-token.
Why is my ~/.docker/config.json auths entry empty?
Docker stores the password in a credential helper when credsStore or credHelpers is set, for example the system keychain. The file then has only {} per registry, and there is nothing to decode.