Private Registry Authentication
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:
imagePullSecretsis 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 |