Private Registry Authentication

3 minute read

Why Private Registries?

Organizations store proprietary container images in private registries that require authentication. Kubernetes needs credentials to pull these images. This is a common CKAD exam scenario.

Common Private Registries

Registry Use Case
Docker Hub (private repos) Personal or team private images
Amazon ECR AWS-hosted workloads
Google Artifact Registry GCP-hosted workloads
Azure Container Registry Azure-hosted workloads
GitHub Container Registry GitHub-integrated projects
Harbor Self-hosted enterprise registry
JFrog Artifactory Enterprise artifact management

Creating a Docker Registry Secret

Kubernetes stores registry credentials in a Secret of type kubernetes.io/dockerconfigjson.

Method 1: kubectl create (Imperative)

This is the fastest approach for the exam:

kubectl create secret docker-registry my-registry-secret \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --docker-email=user@example.com

Parameter breakdown:

Flag Purpose Example
--docker-server Registry hostname registry.example.com
--docker-username Registry username myuser
--docker-password Registry password or token mypassword
--docker-email Email (optional) user@example.com

Method 2: From Existing Docker Config

If you’ve already logged in with docker login, you can create the Secret from your local config:

kubectl create secret generic my-registry-secret \
  --from-file=.dockerconfigjson=$HOME/.docker/config.json \
  --type=kubernetes.io/dockerconfigjson

Method 3: Declarative YAML

apiVersion: v1
kind: Secret
metadata:
  name: my-registry-secret
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-encoded-docker-config>

Generate the base64 value:

# Create the JSON config
echo -n '{"auths":{"registry.example.com":{"username":"myuser","password":"mypassword","auth":"bXl1c2VyOm15cGFzc3dvcmQ="}}}' | base64

Exam tip: Use Method 1 (kubectl create secret docker-registry ...) in the exam. It’s the fastest and least error-prone approach.

Using imagePullSecrets in a Pod

Once the Secret is created, reference it in the Pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: private-app
spec:
  containers:
  - name: app
    image: registry.example.com/team/my-app:v1
  imagePullSecrets:
  - name: my-registry-secret

Key points:

  • imagePullSecrets is at the Pod level (not the container level)
  • You can specify multiple secrets for different registries
  • The kubelet uses the matching secret for the image’s registry

Multiple Registries

apiVersion: v1
kind: Pod
metadata:
  name: multi-registry
spec:
  containers:
  - name: frontend
    image: ghcr.io/myorg/frontend:v1
  - name: backend
    image: registry.example.com/team/backend:v2
  imagePullSecrets:
  - name: ghcr-secret
  - name: internal-registry-secret

Using imagePullSecrets with Deployments

The imagePullSecrets field goes in the Pod template, not the Deployment metadata:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: registry.example.com/team/web:v1
      imagePullSecrets:
      - name: my-registry-secret

ServiceAccount Default Pull Secret

Instead of adding imagePullSecrets to every Pod, you can attach the secret to a ServiceAccount. All Pods using that ServiceAccount will automatically use the credentials.

Step 1: Create the Secret

kubectl create secret docker-registry reg-cred \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword

Step 2: Patch the ServiceAccount

kubectl patch serviceaccount default \
  -p '{"imagePullSecrets": [{"name": "reg-cred"}]}'

Step 3: Verify

kubectl get serviceaccount default -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
imagePullSecrets:
- name: reg-cred

Now any Pod in the namespace using the default ServiceAccount will automatically pull with reg-cred credentials — no imagePullSecrets needed in the Pod spec.

Verifying Registry Secrets

# List secrets
kubectl get secrets

# Inspect the secret
kubectl describe secret my-registry-secret

# Decode the secret content
kubectl get secret my-registry-secret -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq .

Example output:

{
  "auths": {
    "registry.example.com": {
      "username": "myuser",
      "password": "mypassword",
      "auth": "bXl1c2VyOm15cGFzc3dvcmQ="
    }
  }
}

Troubleshooting Private Registry Pulls

Symptom Cause Fix
ErrImagePull with “unauthorized” Missing or wrong credentials Check secret credentials
ErrImagePull with “not found” Wrong image name or registry URL Verify image path
ImagePullBackOff Repeated failures Check all of the above
Pod works but not in new namespace Secret is namespace-scoped Create the secret in the correct namespace

Debug Checklist

# 1. Check Pod events
kubectl describe pod <pod-name> | grep -A 5 "Events:"

# 2. Verify the secret exists in the same namespace
kubectl get secret my-registry-secret -n <namespace>

# 3. Verify the secret has correct data
kubectl get secret my-registry-secret -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d

# 4. Test registry access manually
docker login registry.example.com -u myuser -p mypassword
docker pull registry.example.com/team/my-app:v1

Important: Secrets are namespace-scoped. If your Pod is in namespace prod, the imagePullSecret must also exist in prod.

Quick Reference

Task Command
Create registry secret kubectl create secret docker-registry NAME --docker-server=... --docker-username=... --docker-password=...
Use in Pod Add imagePullSecrets: [{name: NAME}] to Pod spec
Attach to ServiceAccount kubectl patch sa default -p '{"imagePullSecrets":[{"name":"NAME"}]}'
Verify secret kubectl get secret NAME -o jsonpath='{.data.\.dockerconfigjson}' \| base64 -d

Updated: