How to Setup your own Self-Hosted Dockerhub (Private Registry) on Kubernetes

Flow
3 min readApr 19, 2023

A self-hosted private registry. This guide assumes you already have kubernetes up and running.

This guide will deploy both the private registry as well as the web ui.

This is relatively easy so as long as you have the correct configuration. Luckily I did that for you.

Here are the commands.

mkdir -p /kubernetes_config/docker-registry/

kubectl delete namespace docker-registry
kubectl delete -f /kubernetes_config/docker-registry/docker-registry-config.yaml
rm -rf /kubernetes_config/docker-registry/docker-registry-config.yaml
nano /kubernetes_config/docker-registry/docker-registry-config.yaml

[COPY AND PASTE THE YAML FILE BELOW]

kubectl apply -f /kubernetes_config/docker-registry/docker-registry-config.yaml

My External Ip (YOURS WILL BE DIFFERENT):

Docker Registry: 192.168.1.68:5000
Docker Registry Web UI: 192.168.1.71

Here is the full yaml file (REMEMBER TO CHANGE THE URL IN THIS YAML FILE):

apiVersion: v1
kind: Namespace
metadata:
name: docker-registry
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: registry-pvc
namespace: docker-registry
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
name: registry-config
namespace: docker-registry
data:
config.yml: |
version: 0.1
log:
fields:
service: registry
storage:
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
Access-Control-Allow-Origin: ["*"]
Access-Control-Allow-Credentials: [true]
Access-Control-Allow-Headers: ['Authorization', 'Accept', 'Cache-Control']
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-registry
namespace: docker-registry
spec:
replicas: 1
selector:
matchLabels:
app: docker-registry
template:
metadata:
labels:
app: docker-registry
spec:
containers:
- name: registry
image: registry:2
env:
- name: REGISTRY_HTTP_ADDR
value: :5000
- name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
value: /var/lib/registry
- name: REGISTRY_STORAGE_DELETE_ENABLED
value: "true"
ports:
- containerPort: 5000
name: registry
volumeMounts:
- name: registry-storage
mountPath: /var/lib/registry
- name: registry-config
mountPath: /etc/docker/registry
volumes:
- name: registry-storage
persistentVolumeClaim:
claimName: registry-pvc
- name: registry-config
configMap:
name: registry-config
---
apiVersion: v1
kind: Service
metadata:
name: docker-registry
namespace: docker-registry
spec:
selector:
app: docker-registry
ports:
- protocol: TCP
port: 5000
targetPort: 5000
type: LoadBalancer
---
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-registry-ui-config
namespace: docker-registry
data:
registry_url: http://192.168.1.68:5000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-registry-ui
namespace: docker-registry
spec:
replicas: 1
selector:
matchLabels:
app: docker-registry-ui
template:
metadata:
labels:
app: docker-registry-ui
spec:
containers:
- name: docker-registry-ui
image: joxit/docker-registry-ui:latest
ports:
- containerPort: 80
env:
- name: REGISTRY_URL
valueFrom:
configMapKeyRef:
name: docker-registry-ui-config
key: registry_url
- name: DELETE_IMAGES
value: "true"
- name: SINGLE_REGISTRY
value: "true"
---
apiVersion: v1
kind: Service
metadata:
name: docker-registry-ui
namespace: docker-registry
spec:
selector:
app: docker-registry-ui
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
---

When you deploy it. It should work out of the box.

Again, remember to change this line from the yaml file:

  registry_url: http://192.168.1.68:5000

Testing a Docker Image Push to Your Private Registry

mkdir -p /kubernetes_config/docker-registry/test
rm -rf /kubernetes_config/docker-registry/test/Dockerfile
nano /kubernetes_config/docker-registry/test/Dockerfile

[COPY AND PASTE FOLLOWING DOCKERFILE CONTENTS BELOW]

cd /kubernetes_config/docker-registry/test
docker build -t test-dockerimage-1 .
docker tag test-dockerimage-1 192.168.1.68:5000/test-dockerimage-1
docker push 192.168.1.68:5000/test-dockerimage-1

Remember to change the ip address your ip address when using the commands

Dockerfile:

FROM alpine:3.17.1

This is to see if the image is pushed to the registry:

curl -s http://192.168.1.68:5000/v2/_catalog | jq '.repositories[]'
What it should look like

And then if you go to the website: 192.168.1.71:80

You should see the image there as well.

DONE!

--

--