πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Troubleshooting beginner ⏱ 10 minutes K8s 1.24+

Decode and Inspect Kubernetes Docker Secrets

Decode base64-encoded dockerconfigjson secrets to verify registry credentials, troubleshoot ImagePullBackOff errors, and audit pull secret configurations.

By Luca Berton β€’ β€’ Updated February 26, 2026 β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: Use kubectl get secret <name> -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq to decode any docker-registry Secret, then pipe the auth field through base64 -d again to see the plain username:password.

The Problem

You’re troubleshooting ImagePullBackOff errors and need to verify that your Kubernetes pull secret contains the right registry credentials. But the secret data is double base64-encoded:

  1. The .dockerconfigjson value is base64-encoded (Kubernetes stores all secret data in base64)
  2. Inside the decoded JSON, each auth field is also base64-encoded (base64(username:password))

You need to decode both layers to inspect the actual credentials.

The Solution

Decode the Full Secret

# Decode the entire .dockerconfigjson
kubectl get secret my-pull-secret \
  -n my-namespace \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq .

Output:

{
  "auths": {
    "quay.io": {
      "auth": "bXlvcmcrazhzX3Byb2RfcHVsbGVyOkFCQzEyMw==",
      "email": "robot@myorg.example.com"
    }
  }
}

Decode the Auth Credential

The auth field is base64(username:password) β€” decode it to see the actual credentials:

# Extract and decode the auth for a specific registry
kubectl get secret my-pull-secret \
  -n my-namespace \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d \
  | jq -r '.auths["quay.io"].auth' \
  | base64 -d

Output:

myorg+k8s_prod_puller:ABC123

One-Liner: Show All Registries and Credentials

# Decode all registry credentials in a secret
kubectl get secret my-pull-secret -n my-namespace \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d \
  | jq -r '.auths | to_entries[] | "\(.key) β†’ \(.value.auth | @base64d)"'

Output:

quay.io β†’ myorg+k8s_prod_puller:ABC123ROBOTTOKEN
gcr.io β†’ _json_key:{...service-account-json...}

OpenShift: Inspect the Cluster-Wide Pull Secret

# Decode the cluster-wide pull secret
oc get secret pull-secret -n openshift-config \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq .

# Decode a specific registry's auth
oc get secret pull-secret -n openshift-config \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d \
  | jq -r '.auths["quay.internal.example.com"].auth' | base64 -d

Alternative: Decode with Python

If jq isn’t available:

python3 -c "
import base64, json, sys
data = json.loads(base64.b64decode(sys.stdin.read()))
for host, creds in data['auths'].items():
    username_password = base64.b64decode(creds['auth']).decode()
    print(f'{host} β†’ {username_password}')
" <<< "$(kubectl get secret my-pull-secret -n my-namespace -o jsonpath='{.data.\.dockerconfigjson}')"

Audit All Pull Secrets in a Namespace

#!/bin/bash
# audit-pull-secrets.sh β€” List all pull secrets and their registries

NAMESPACE="${1:-default}"

echo "=== Pull secrets in namespace: $NAMESPACE ==="
for secret in $(kubectl get secrets -n "$NAMESPACE" \
  -o jsonpath='{range .items[?(@.type=="kubernetes.io/dockerconfigjson")]}{.metadata.name}{"\n"}{end}'); do

  echo ""
  echo "Secret: $secret"
  kubectl get secret "$secret" -n "$NAMESPACE" \
    -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d \
    | jq -r '.auths | to_entries[] | "  Registry: \(.key)  User: \(.value.auth | @base64d | split(":")[0])"'
done

Output:

=== Pull secrets in namespace: production ===

Secret: quay-pull-secret
  Registry: quay.io  User: myorg+k8s_prod_puller

Secret: gcr-pull-secret
  Registry: gcr.io  User: _json_key
flowchart LR
    A[K8s Secret<br/>type: dockerconfigjson] -->|"base64 -d (layer 1)"| B[JSON with auths]
    B -->|"jq .auths[].auth"| C[auth field<br/>base64 encoded]
    C -->|"base64 -d (layer 2)"| D["username:password<br/>in plaintext"]

Common Issues

jq Not Installed

Use python3 as shown above, or decode manually:

# Without jq β€” raw decode
kubectl get secret my-pull-secret -n my-namespace \
  -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d

Permission Denied

You need get permission on secrets in the target namespace:

kubectl auth can-i get secrets -n my-namespace

Secret Type Mismatch

Ensure you’re inspecting a kubernetes.io/dockerconfigjson type secret, not Opaque:

kubectl get secret my-pull-secret -n my-namespace -o jsonpath='{.type}'

Best Practices

  • Never log decoded credentials β€” inspect interactively, don’t pipe to files or CI logs
  • Use jq for safe parsing β€” don’t rely on string splitting for JSON
  • Audit pull secrets periodically β€” verify credentials haven’t expired or been rotated
  • Check both layers β€” a valid-looking secret can still have wrong credentials in the auth field
  • Clean up shell history β€” history -d or history -c after decoding sensitive data

Key Takeaways

  • Docker registry secrets have two layers of base64: Kubernetes secret encoding and the auth field itself
  • Use base64 -d | jq to decode the first layer, then base64 -d again for the auth credential
  • The auth field format is always base64(username:password) separated by a colon
  • Audit all pull secrets in a namespace with a simple loop script
#secrets #base64 #troubleshooting #debugging #container-registry
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens