Kubernetes
Google Cloud
Docker Hub
Private Registry
Container Orchestration

Google Cloud Kubernetes accessing private Docker Hub hosted images

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

GKE can pull private images from Docker Hub, but only if the Pods have credentials they can use during image pull. The standard Kubernetes solution is to create an image pull secret in the target namespace and reference it from the Pod spec or from the ServiceAccount used by the workload.

Create a Docker registry secret

Start by creating a docker-registry secret with your Docker Hub credentials:

bash
1kubectl create secret docker-registry regcred \
2  --docker-server=https://index.docker.io/v1/ \
3  --docker-username="$DOCKERHUB_USERNAME" \
4  --docker-password="$DOCKERHUB_TOKEN"

Using a Docker Hub access token is better than using a personal password. The secret must exist in the same namespace as the workload that needs it. If your app runs in production, create the secret there:

bash
1kubectl create secret docker-registry regcred \
2  --namespace production \
3  --docker-server=https://index.docker.io/v1/ \
4  --docker-username="$DOCKERHUB_USERNAME" \
5  --docker-password="$DOCKERHUB_TOKEN"

Reference the secret from the Pod or Deployment

The simplest way is to add imagePullSecrets to your Pod template:

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: web
5spec:
6  replicas: 2
7  selector:
8    matchLabels:
9      app: web
10  template:
11    metadata:
12      labels:
13        app: web
14    spec:
15      imagePullSecrets:
16        - name: regcred
17      containers:
18        - name: web
19          image: docker.io/your-user/private-web:1.0.0
20          ports:
21            - containerPort: 8080

When the Pod starts, kubelet uses the secret to authenticate to Docker Hub and pull the image.

Attach the secret to a ServiceAccount for reuse

If several Deployments in the same namespace need the same registry credentials, attach the secret to a ServiceAccount instead of repeating imagePullSecrets in every manifest:

yaml
1apiVersion: v1
2kind: ServiceAccount
3metadata:
4  name: private-images
5imagePullSecrets:
6  - name: regcred
7---
8apiVersion: apps/v1
9kind: Deployment
10metadata:
11  name: api
12spec:
13  selector:
14    matchLabels:
15      app: api
16  template:
17    metadata:
18      labels:
19        app: api
20    spec:
21      serviceAccountName: private-images
22      containers:
23        - name: api
24          image: docker.io/your-user/private-api:2.3.1

That keeps the deployment manifests cleaner and centralizes the credential reference.

Verify the pull path when it fails

If the image still does not pull, describe the Pod:

bash
kubectl describe pod web-1234567890-abcd

Look for events such as:

  • 'ErrImagePull'
  • 'ImagePullBackOff'
  • unauthorized or denied errors from Docker Hub

Also check these basics:

  • the secret is in the correct namespace
  • the image name is exactly correct
  • the token still works
  • the Pod template actually references the secret or ServiceAccount

Kubernetes does not infer any of this automatically just because the cluster runs on Google Cloud. GKE manages Kubernetes infrastructure, but Docker Hub authentication is still your responsibility.

Consider operational tradeoffs

Using Docker Hub for private images works, but it has a few drawbacks in production:

  • credentials must be rotated and updated in the cluster
  • Docker Hub pull limits and registry availability still matter
  • image delivery is external to Google Cloud

If the workloads are primarily on GKE, Artifact Registry is often a cleaner long-term option. Still, for existing private Docker Hub images, imagePullSecrets is the standard and correct approach.

Common Pitfalls

The biggest mistake is creating the secret in the default namespace while the workload runs in a different namespace. Kubernetes secrets are namespace-scoped.

Another common problem is using the right secret but forgetting to reference it in the Pod template or ServiceAccount.

People also confuse node access with Pod access. Even if your cluster nodes can reach Docker Hub over the network, that does not mean kubelet has valid credentials for a private repository.

Finally, avoid using personal passwords when a scoped Docker Hub token or service account is available. Rotating credentials later is much easier that way.

Summary

  • Create a docker-registry secret with Docker Hub credentials in the correct namespace.
  • Reference the secret through imagePullSecrets or a shared ServiceAccount.
  • Use kubectl describe pod to diagnose ErrImagePull and ImagePullBackOff events.
  • Remember that GKE does not automatically authenticate to private Docker Hub repositories.
  • Consider Artifact Registry for a tighter long-term fit with Google Cloud workloads.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.