Containers Advanced

Kubernetes ImagePullBackOff: Resolving Private Registry Authentication on Ubuntu 20.04 LTS

Troubleshoot and resolve Kubernetes ImagePullBackOff errors caused by private registry authentication failures on Ubuntu 20.04 LTS clusters. Learn to configure ImagePullSecrets correctly.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Troubleshoot and resolve Kubernetes ImagePullBackOff errors caused by private registry authentication failures on Ubuntu 20.04 LTS clusters. Learn to configure ImagePullSecrets correctly.

Kubernetes ImagePullBackOff: Resolving Private Registry Authentication on Ubuntu 20.04 LTS

The ImagePullBackOff error is a common headache for Kubernetes administrators, indicating that a pod is unable to pull its container image. While this error can stem from various sources like incorrect image names or network issues, a particularly challenging variant arises when attempting to pull images from a private container registry. This guide focuses on diagnosing and resolving ImagePullBackOff specifically due to authentication failures with private registries on an Ubuntu 20.04 LTS Kubernetes cluster.

When this issue occurs, your pods will remain in a Pending or ImagePullBackOff state, preventing your applications from deploying correctly. The root cause typically lies in Kubernetes not having the necessary credentials to authenticate with your private Docker, GitLab, Harbor, or other private container registry.

Symptom & Error Signature

You will observe pods stuck in a non-running state when checking their status:

kubectl get pods
NAME                             READY   STATUS             RESTARTS   AGE
my-app-deployment-78f99c7d4d-abcde   0/1     ImagePullBackOff   0          2m

A more detailed examination of the pod will reveal the underlying authentication failure. Use kubectl describe to pinpoint the exact error:

kubectl describe pod my-app-deployment-78f99c7d4d-abcde

Look for events similar to these in the output:

Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  2m                   default-scheduler  Successfully assigned default/my-app-deployment-78f99c7d4d-abcde to node01
  Normal   Pulling    1m                   kubelet            Pulling image "my-private-registry.com/my-org/my-app:1.0"
  Warning  Failed     30s                  kubelet            Failed to pull image "my-private-registry.com/my-org/my-app:1.0": rpc error: code = Unknown desc = Error response from daemon: Get "https://my-private-registry.com/v2/": unauthorized: authentication required
  Warning  Failed     30s                  kubelet            Error: ErrImagePull
  Normal   BackOff    15s                  kubelet            Back-off pulling image "my-private-registry.com/my-org/my-app:1.0"
  Warning  Failed     15s                  kubelet            Error: ImagePullBackOff

The crucial part here is unauthorized: authentication required, which clearly points to a credentials issue when the Kubelet attempts to pull the image from your private registry.

Root Cause Analysis

The ImagePullBackOff error stemming from unauthorized: authentication required indicates that the Kubernetes cluster's node (specifically the Kubelet) cannot authenticate with the specified private container registry. The primary reasons for this are:

  1. Missing ImagePullSecret: Kubernetes deployments requiring images from private registries must be explicitly told how to authenticate using ImagePullSecrets. If this secret is absent or not correctly referenced, authentication will fail.
  2. Incorrect ImagePullSecret: The ImagePullSecret exists, but its contents (username, password, or authentication token) are invalid, expired, or belong to an account that lacks pull permissions for the desired image.
  3. ImagePullSecret Not Linked to Pod/ServiceAccount:
    • The ImagePullSecret is created but not explicitly referenced in the Pod's spec.imagePullSecrets array.
    • If leveraging a Kubernetes ServiceAccount for authentication (a recommended practice), the ImagePullSecret might not be added to the ServiceAccount.imagePullSecrets array, or the Pod is not configured to use the correct ServiceAccount.
  4. Registry URL Mismatch: The full registry URL specified in the ImagePullSecret (e.g., my-private-registry.com) does not precisely match the registry URL used in the Pod's image definition (e.g., my-private-registry.com/my-org/my-app:1.0). This includes subtle differences in hostnames or port numbers.
  5. DNS/Network Issues (Masked): While less common for an explicit "unauthorized" message, initial ImagePullBackOff might sometimes stem from a failure to even reach the registry. This could be due to DNS resolution failures for the registry hostname or network firewall rules blocking outbound connections from your Kubernetes nodes to the private registry.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the ImagePullBackOff error due to private registry authentication on your Ubuntu 20.04 LTS Kubernetes cluster.

1. Verify Pod Status and Detailed Error

Start by re-confirming the error message and current pod status.

kubectl get pods
kubectl describe pod <pod-name-from-get-pods>

Focus on the Events section for explicit unauthorized: authentication required messages. This confirms we are dealing with an authentication issue rather than a network or image name problem.

2. Confirm Private Registry Access Manually from a Node

It's crucial to rule out basic connectivity or credential issues outside of Kubernetes. SSH into one of your Kubernetes worker nodes (or the control plane if it's also a worker) and attempt to log in to and pull the image from the private registry manually. This verifies that the node itself can reach the registry and that your credentials are valid.

# SSH into a Kubernetes worker node (replace with your node's IP/hostname)
ssh your_username@<worker-node-ip>

# Login to your private registry using Docker CLI
# If you are using containerd as your runtime, this still verifies credentials
docker login my-private-registry.com
# You will be prompted for Username and Password.
# Enter the same credentials you intend to use in your Kubernetes secret.

If docker login fails here, the problem is with your credentials or the registry itself, not Kubernetes. Rectify this before proceeding.

# Attempt to pull the image manually
docker pull my-private-registry.com/my-org/my-app:1.0

If the manual pull succeeds, your credentials and network connectivity from the node are confirmed, indicating the issue is specific to Kubernetes' configuration. If it fails, troubleshoot your Docker daemon's configuration, network access from the node, or the registry settings.

3. Create or Verify Kubernetes ImagePullSecret

Kubernetes needs a docker-registry type secret to store your private registry credentials.

Creating a new ImagePullSecret:
kubectl create secret docker-registry my-registry-secret 
  --docker-server=my-private-registry.com 
  --docker-username=your-registry-username 
  --docker-password=your-registry-password 
  [email protected] 
  --namespace=default

Replace my-registry-secret, my-private-registry.com, your-registry-username, your-registry-password, [email protected], and default with your actual values and desired namespace.

The --docker-server value my-private-registry.com must exactly match the hostname part of the image name specified in your pod's manifest (e.g., my-private-registry.com/my-org/my-app:1.0). Case sensitivity might also be a factor for some registries.

Verifying an existing ImagePullSecret:

If you believe the secret already exists, inspect its contents to ensure the credentials are correct and properly encoded.

kubectl get secret my-registry-secret -o yaml -n default

Look for the data field and specifically the .dockerconfigjson key. Its value is Base64 encoded. Decode it to verify the embedded credentials.

# Example of decoding the .dockerconfigjson
kubectl get secret my-registry-secret -o jsonpath='{.data..dockerconfigjson}' -n default | base64 --decode

This should output a JSON similar to {"auths":{"my-private-registry.com":{"username":"...","password":"...","email":"...","auth":"..."}}}. Verify the username, password, and my-private-registry.com URL.

4. Link ImagePullSecret to Your Pod or ServiceAccount

Once the ImagePullSecret is confirmed to be correct, you must instruct Kubernetes pods to use it. There are two primary methods:

Option A: Directly in the Pod/Deployment Specification (Quick Fix)

Add the imagePullSecrets field directly to your Deployment or Pod manifest.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
  labels:
    app: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-private-registry.com/my-org/my-app:1.0
        ports:
        - containerPort: 80
      imagePullSecrets:             # <--- Add this section
      - name: my-registry-secret    # <--- Name of your ImagePullSecret

Apply this updated manifest:

kubectl apply -f deployment.yaml
Option B: Via ServiceAccount (Recommended Best Practice)

This method automatically applies the ImagePullSecret to all pods using a specific ServiceAccount in a given namespace, reducing repetition and improving manageability.

First, ensure you have a ServiceAccount or create one:

# serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-app-sa
  namespace: default
imagePullSecrets:
- name: my-registry-secret # <--- Link the ImagePullSecret here

Apply the ServiceAccount:

kubectl apply -f serviceaccount.yaml

Next, configure your Deployment to use this ServiceAccount:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
  labels:
    app: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      serviceAccountName: my-app-sa # <--- Specify the ServiceAccount
      containers:
      - name: my-app
        image: my-private-registry.com/my-org/my-app:1.0
        ports:
        - containerPort: 80
      # No need for imagePullSecrets directly in the pod spec if using ServiceAccount

Apply the updated deployment:

kubectl apply -f deployment.yaml

If you are using the default ServiceAccount in the namespace and want all pods to inherit the secret, you can patch the default ServiceAccount: kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "my-registry-secret"}]}' -n default This is often done in development but can be less secure for production. Prefer dedicated ServiceAccounts.

5. Verify and Redeploy

After applying the changes, you need to force Kubernetes to re-evaluate and pull the image. The easiest way is to delete the problematic pod(s) or trigger a rolling restart of the deployment.

# Option 1: Delete individual pod (if you have few)
kubectl delete pod <pod-name-from-get-pods>

# Option 2: Trigger a rolling restart for a deployment (recommended)
kubectl rollout restart deployment my-app-deployment -n default

Monitor the status of your pods:

kubectl get pods -w

You should see the new pods being created and eventually transitioning to Running status without the ImagePullBackOff error. If it still fails, use kubectl describe pod <new-pod-name> again to check for new errors.

6. Troubleshoot Persistent Issues

If the ImagePullBackOff error persists, consider these advanced troubleshooting steps:

  • Registry URL Case Sensitivity: Ensure the registry URL in your ImagePullSecret and your image definition are identical, including case. Some registries are particular.
  • Network Connectivity (Deep Dive): If the docker login on the node seemed fine but Kubelet still struggles, there might be subtle network issues. Check your Kubernetes network policies, Ubuntu's ufw firewall (if active on nodes), or cloud provider security groups to ensure outbound traffic to my-private-registry.com:443 is permitted from your worker nodes.
  • Expired/Revoked Credentials: Double-check with your registry administrator if the credentials embedded in the ImagePullSecret are still active and have the necessary pull permissions.
  • Cloud-Specific Registries (GCR, ACR, ECR):
    • GCR (Google Container Registry): Typically uses a JSON key file for service accounts. The docker-username is _json_key and the docker-password is the entire JSON key file content (escaped). Or, use gcloud auth configure-docker.
    • ACR (Azure Container Registry): Requires a service principal.
    • ECR (Amazon Elastic Container Registry): Requires specific AWS IAM roles and temporary credentials, often managed by an ECR credential helper or an IAM role attached to the worker nodes. ImagePullSecrets can be used, but require generating temporary Docker login credentials. Ensure your ImagePullSecret creation method aligns with the specific authentication mechanism of your cloud-based private registry.
  • Container Runtime Issues (e.g., containerd): While Kubernetes ImagePullSecrets are runtime-agnostic, if you're manually debugging on a node with crictl, remember it doesn't always automatically pick up ~/.docker/config.json. For Kubernetes-managed pods, the ImagePullSecret mechanism is the primary interaction point with the runtime.
  • Check Kubelet Logs: For deep debugging, you can check the Kubelet logs on the affected node.
    journalctl -u kubelet -f --since "5 minutes ago"
    # Or, if using Docker as runtime:
    journalctl -u docker -f --since "5 minutes ago"
    
    This might reveal errors from the container runtime when it attempts to pull the image, offering more granular detail than kubectl describe.

By systematically working through these steps, you should be able to identify and rectify the authentication problem preventing your Kubernetes pods from pulling images from your private registry on Ubuntu 20.04 LTS.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.