Docker
Kubernetes
Vagrant
Private Repository
DevOps

How to access private Docker Hub repository from Kubernetes on Vagrant

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

To pull images from a private Docker Hub repository in Kubernetes running on Vagrant, you need to create a Kubernetes Secret containing your Docker Hub credentials and reference it in your pod spec with imagePullSecrets. Without this, Kubernetes gets ErrImagePull or ImagePullBackOff because the kubelet cannot authenticate with Docker Hub. The process is the same regardless of whether Kubernetes runs on Vagrant, cloud VMs, or bare metal.

Step 1: Create a Docker Registry Secret

bash
1kubectl create secret docker-registry dockerhub-secret \
2  --docker-server=https://index.docker.io/v1/ \
3  --docker-username=YOUR_DOCKERHUB_USERNAME \
4  --docker-password=YOUR_DOCKERHUB_PASSWORD \
5  --docker-email=[email protected]

This creates a Secret named dockerhub-secret in the current namespace containing your Docker Hub credentials.

Step 2: Reference the Secret in Pod Spec

yaml
1apiVersion: v1
2kind: Pod
3metadata:
4  name: my-app
5spec:
6  containers:
7    - name: my-app
8      image: yourusername/private-repo:latest
9      ports:
10        - containerPort: 8080
11  imagePullSecrets:
12    - name: dockerhub-secret

The imagePullSecrets field tells the kubelet to use the Secret's credentials when pulling the image.

Step 3: Apply and Verify

bash
1kubectl apply -f pod.yaml
2kubectl get pods
3# NAME     READY   STATUS    RESTARTS   AGE
4# my-app   1/1     Running   0          30s
5
6# If it fails:
7kubectl describe pod my-app
8# Events:
9#   Warning  Failed   kubelet  Failed to pull image: unauthorized

Using with Deployments

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: my-app
5spec:
6  replicas: 3
7  selector:
8    matchLabels:
9      app: my-app
10  template:
11    metadata:
12      labels:
13        app: my-app
14    spec:
15      containers:
16        - name: my-app
17          image: yourusername/private-repo:v1.0
18          ports:
19            - containerPort: 8080
20      imagePullSecrets:
21        - name: dockerhub-secret

Creating the Secret from Docker Config

If you have already logged in with docker login:

bash
1# Docker stores credentials in ~/.docker/config.json
2cat ~/.docker/config.json
3# {
4#   "auths": {
5#     "https://index.docker.io/v1/": {
6#       "auth": "base64encoded..."
7#     }
8#   }
9# }
10
11# Create the secret from this file
12kubectl create secret generic dockerhub-secret \
13  --from-file=.dockerconfigjson=$HOME/.docker/config.json \
14  --type=kubernetes.io/dockerconfigjson

Personal access tokens are more secure than passwords:

bash
1# Create a token at https://hub.docker.com/settings/security
2
3kubectl create secret docker-registry dockerhub-secret \
4  --docker-server=https://index.docker.io/v1/ \
5  --docker-username=YOUR_USERNAME \
6  --docker-password=dckr_pat_YOUR_ACCESS_TOKEN \
7  --docker-email=[email protected]

Access tokens can be scoped and revoked without changing your password.

Attaching Secret to a Service Account

Instead of adding imagePullSecrets to every pod, attach it to the default service account:

bash
kubectl patch serviceaccount default \
  -p '{"imagePullSecrets": [{"name": "dockerhub-secret"}]}'

Now all pods using the default service account automatically use the credentials:

yaml
1# No imagePullSecrets needed in the pod spec
2apiVersion: v1
3kind: Pod
4metadata:
5  name: my-app
6spec:
7  containers:
8    - name: my-app
9      image: yourusername/private-repo:latest

Vagrant-Specific Considerations

Networking

ruby
1# Vagrantfile: ensure nodes can reach Docker Hub
2Vagrant.configure("2") do |config|
3  config.vm.box = "ubuntu/focal64"
4
5  config.vm.provider "virtualbox" do |vb|
6    vb.memory = "4096"
7    vb.cpus = 2
8  end
9
10  # Bridge network for internet access
11  config.vm.network "public_network"
12
13  # Or NAT with port forwarding
14  config.vm.network "forwarded_port", guest: 8080, host: 8080
15end

Vagrant VMs need internet access to pull images from Docker Hub. If using a private network, configure a proxy or pre-pull images.

Pre-Pulling Images

bash
1# SSH into Vagrant VM and pre-pull
2vagrant ssh
3docker login -u YOUR_USERNAME -p YOUR_TOKEN
4docker pull yourusername/private-repo:latest

Pre-pulling avoids repeated downloads and works offline after the initial pull.

Multiple Registries

yaml
1spec:
2  containers:
3    - name: app
4      image: yourusername/app:latest
5    - name: sidecar
6      image: gcr.io/my-project/sidecar:v1
7  imagePullSecrets:
8    - name: dockerhub-secret
9    - name: gcr-secret

List multiple secrets for pods that pull from different registries.

Verifying the Secret

bash
1# Check the secret exists
2kubectl get secrets dockerhub-secret
3
4# Decode and inspect (base64)
5kubectl get secret dockerhub-secret -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d
6
7# Test pulling manually inside a pod
8kubectl run test --image=yourusername/private-repo:latest \
9  --overrides='{"spec":{"imagePullSecrets":[{"name":"dockerhub-secret"}]}}' \
10  --rm -it -- /bin/sh

Namespace Scope

Secrets are namespace-scoped. Create the secret in each namespace that needs it:

bash
1# Create in specific namespace
2kubectl create secret docker-registry dockerhub-secret \
3  --docker-server=https://index.docker.io/v1/ \
4  --docker-username=YOUR_USERNAME \
5  --docker-password=YOUR_TOKEN \
6  --namespace=staging
7
8# Or copy from default namespace
9kubectl get secret dockerhub-secret -o yaml | \
10  sed 's/namespace: default/namespace: staging/' | \
11  kubectl apply -f -

Common Pitfalls

  • Wrong docker-server URL: For Docker Hub, use https://index.docker.io/v1/. Other registries have different URLs (e.g., ghcr.io, gcr.io). A wrong URL causes authentication failure even with correct credentials.
  • Secret in wrong namespace: If your pod is in namespace staging but the secret is in default, the pod cannot access it. Create the secret in the same namespace as the pod.
  • Expired access token: Docker Hub access tokens can expire. If pulls suddenly fail with unauthorized, regenerate the token and update the secret.
  • Rate limiting: Docker Hub limits anonymous pulls to 100/6h and authenticated pulls to 200/6h. If you hit limits on Vagrant (multiple nodes pulling), use a pull-through cache or pre-pull images.
  • Vagrant VM memory: Kubernetes + Docker + your application needs at least 4GB RAM. Underpowered VMs cause OOMKilled errors that look like image pull failures.

Summary

  • Create a docker-registry Secret with Docker Hub credentials
  • Add imagePullSecrets to the pod spec or patch the default service account
  • Use Docker Hub access tokens instead of passwords for better security
  • Secrets are namespace-scoped. Create them in each namespace that needs them
  • Ensure Vagrant VMs have internet access and sufficient resources (4GB+ RAM)
  • Attach secrets to the default service account to avoid repeating imagePullSecrets in every pod

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.