
Introduction
Hosting a Docker private registry gives you full control over the storage location of your images and how you can access them. This is especially useful if you develop private projects that shouldn’t be publicly on Docker Hub.
In this tutorial, you will learn how to install a private Docker registry in any Kubernetes cluster. It is a follow-up of a tutorial previously released on this blog because it will use a Traefik Ingress Controller to expose the Docker registry.
Preparation
Create Kubernetes Namespace
The first step is to create a Kubernetes namespace where all resources will be applied during this tutorial:
kubectl create namespace docker-registry
PersistentVolumeClaim
In this section, you will mount a volume into a dedicated Kubernetes Pod using a PersistentVolumeClaim. A PersistentVolumeClaim (PVC
) is a Kubernetes resource to use a pre-defined abstract PersistentVolume (PV
) storage without exposing how those volumes are implemented.
💡 Prerequisite: A PersistentVolume should exist within the Kubernetes cluster. A PersistentVolume (PV
) is a storage definition that was either manually provisioned by a user or dynamically provisioned using a storage class. See this link for further information.
To set up the PersistentVolumeClaim that will be used in the private Docker registry, this tutorial will assume that you already have a preconfigured PersistentVolume called csi-cinder-classic
.
- Create a new file (
registry-pvc.yaml
) containing the PersistentVolumeClaim using thecsi-cinder-classic
storage class:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: docker-registry-pv-claim
namespace: docker-registry
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 60Gi
storageClassName: csi-cinder-classic
- Apply it to the Kubernetes Cluster:
kubectl apply -f registry-pvc.yamlb
- Verify if the persistent volume claim was successfully created:
kubectl get pvc docker-registry-pv-claim -n docker-registry
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
# docker-registry-pv-claim Bound *** 60Gi RWO csi-cinder-classic
Set Up Docker Registry
Now, that you have created the namespace and the PersistentVolumeClaim you can deploy a Docker registry using the external storage and make the registry available across the entire Kubernetes cluster.
Generate User & Password
To set up the Docker registry, you need to generate a user and password that will be used to push and pull images to the registry.
Create a new file gen-pass.sh
that contains the following content:
export REGISTRY_USER=admin
export REGISTRY_PASS=registryPass
export DESTINATION_FOLDER=./registry-creds
# Backup credentials to local files (in case you'll forget them later on)
mkdir -p ${DESTINATION_FOLDER}
echo ${REGISTRY_USER} >> ${DESTINATION_FOLDER}/registry-user.txt
echo ${REGISTRY_PASS} >> ${DESTINATION_FOLDER}/registry-pass.txt
docker run --entrypoint htpasswd registry:2.7.0
-Bbn ${REGISTRY_USER} ${REGISTRY_PASS}
> ${DESTINATION_FOLDER}/htpasswd
unset REGISTRY_USER REGISTRY_PASS DESTINATION_FOLDER
💡 Note: _Change REGISTRY_USER and REGISTRYPASS to your needs. Also, since htpasswd
was removed from the latest Docker images we will be using version 2.7.0 to generate the user and password. For more information have a look at this stack overflow post.
Execute the script which will create the credentials within the subfolder ./registry-creds
:
sh gen-pass.sh
Install Docker Registry Using Helm
In the following steps, you will use Helm to install the Docker registry.
💡 Note: Helm is the package manager for Kubernetes, focused on automating the installation of all kinds of Kubernetes applications.
- Add the
twuni/docker-registry
Helm repository that is a successor of the official Docker registry:
helm repo add twuni https://helm.twun.io
- Update local Helm chart repository cache:
helm repo update
- Search for the latest version of
twuni/docker-registry
Helm chart version:
helm search repo docker-registry
# NAME CHART VERSION APP VERSION DESCRIPTION
# twuni/docker-registry 2.2.2 2.8.1 A Helm chart for Docker Registry
- Create a
registry-chart.yaml
which will be used to install the Docker registry in our Kubernetes cluster:
---
replicaCount: 1
persistence:
enabled: true
size: 60Gi
deleteEnabled: true
storageClass: csi-cinder-classic
existingClaim: docker-registry-pv-claim
secrets:
htpasswd: admin:$2y$05$Gh/3ppmkuIXJIVyBBtHf0ug.wnnJvbtSEzlXz6z/7oO7XvF/xq7Ni
💡 Note: Replace the htpasswd
string with the content of the file that was generated by the gen-pass.sh
script (./registry-cred/htpasswd
)
- Install the Docker registry Helm chart using the
registry-chart.yaml
:
helm install -f .registry-chart.yaml docker-registry --namespace docker-registry twuni/docker-registry
- Verify installation:
kubectl get pods -n docker-registry
# NAME READY STATUS RESTARTS
# docker-registry-9fa1234ba-gaf16 1/1 Running 0
If you want to change something like the replicaCount
, the htpasswd
or the storage, you can do this by adjusting the registry-chart.yaml
and reapply it by executing:
helm upgrade -f .registry-chart.yaml docker-registry --namespace docker-registry twuni/docker-registry
How To Uninstall?
To uninstall the Docker registry, you have to remove it from the Kubernetes cluster with Helm:
helm uninstall docker-registry --namespace docker-registry
Then remove the Docker registry namespace from the Kubernetes cluster:
kubectl delete namespace docker-registry
Add Docker Registry Ingress
To expose the Docker registry, you will use Traefik Ingress Controller to allow access to the registry through HTTPS with a proper TLS certificate.
💡 Note: Read about how to install Traefik Ingress Controller in any Kubernetes cluster within this tutorial: https://www.paulsblog.dev/how-to-install-traefik-ingress-controller-in-kubernetes/
- Create a new file (
ingressroute.yaml
) and make sure to replace YOUR_DOMAIN with your Docker registry domain:
---
kind: IngressRoute
apiVersion: traefik.containo.us/v1alpha1
metadata:
name: docker-registry
namespace: docker-registry
spec:
entryPoints:
- websecure
routes:
- match: Host(`YOUR_DOMAIN`)
kind: Rule
services:
- name: docker-registry
port: 5000
- Apply the IngressRoute by executing:
kubectl apply -f ingressroute.yaml
- Verify if the Kubernetes resource was successfully created:
kubectl describe ingressroute docker-registry -n docker-registry
Push Images To Private Registry In Kubernetes Cluster
To show how to push a Docker Image to our new Docker registry, this tutorial will show how to pull a public Docker Hub image, tag it, and then push it to your registry.
- Log in to the Docker registry by using the previously created credentials
docker login
-u $(cat ./registry-creds/registry-user.txt)
-p $(cat ./registry-creds/registry-pass.txt)
YOUR_DOMAIN
- Pull the
nginx:latest
Docker image:
docker pull nginx
- Tag the image with a custom name and prepend the private Docker registry domain name
docker tag nginx YOUR_DOMAIN/my-nginx
- Push the Docker Image to the registry
docker push YOUR_DOMAIN/my-nginx
Use Docker Registry To Pull Images In Your Kubernetes Cluster
As you have a Docker registry deployed in your Kubernetes cluster, you can start using it by pulling previously pushed images for your Kubernetes Pods.
To learn how a private Docker registry can be used for pulling images, you will create a simple Kubernetes pod in a new test
namespace. This Kubernetes Pod will use the previously pushed image YOUR_DOMAIN/my-nginx
.
First, you have to create the test
Kubernetes namespace:
kubectl create namespace test
Create Docker Registry Secret
This is the most important step!
You have to create a Docker Secret for accessing the Docker registry from your Kubernetes cluster. To do this, use the credentials from the previous step and create a Kubernetes Docker Secret in the test
namespace:
kubectl create secret docker-registry regcred --docker-server=YOUR_DOMAIN --docker-username=admin --docker-password=registryPass -n test
💡 Note: This Kubernetes Docker secret resource must be created within the correct namespace!
Create Kubernetes Pod With Image From Docker Registry
After the Kubernetes secret containing the credentials for the Docker registry is created, you create a new Kubernetes Deployment (test-nginx.yaml
) that uses your registry:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: test
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: YOUR_DOMAIN/my-nginx
ports:
- containerPort: 80
imagePullSecrets:
- name: regcred
If taking a close look at this file, you will see that it contains three essential differences from a normal nginx
deployment.
image: YOUR_DOMAIN/my-nginx
-
`imagePullSecrets:
- name: regred`
Both changes are necessary to use the Docker registry. The first option selects the image to use in the pod (registry URL as a prefix + image name). The second option sets which Docker secret is used to pull the image. If you changed credentials or names, you have to update both values.
Now, deploy the Kubernetes Deployment in your Cluster:
kubectl apply -f test-nginx.yaml
Use kubectl describe podname -n test
to see if the image will be pulled and the container gets started correctly. The output should look like this:

Closing Notes
Hopefully, this article gave you a good overview of how to set up a private Docker registry in your Kubernetes cluster.
Remember, having a Private Docker registry is essential if deploying Docker services that are not Open Source!
To have an easy, repeatable procedure that you can run on any Kubernetes cluster, I created a GitHub Gist containing all necessary files. If you already have a Traefik Ingress Controller running in your Kubernetes cluster, you can simply download all files, adjust them to your needs, and then apply them by executing:
kubectl create namespace docker-registry
kubectl apply -f registry-pvc.yaml
helm repo add twuni https://helm.twun.io
helm repo update
helm search repo docker-registry
helm install -f values.yaml docker-registry --namespace docker-registry twuni/docker-registry
kubectl apply -f registry-ingressroute.yaml
I would love to hear your feedback about this tutorial. Furthermore, if you also have set up a Docker registry and use a different approach, please comment here and explain what you have done differently. Also, if you have any questions, please ask them in the comments. I will answer them if possible.
Feel free to connect with me on my blog, LinkedIn, Twitter, and GitHub.
This article was initially published on my blog at https://www.paulsblog.dev/how-to-install-a-private-docker-container-registry-in-kubernetes/