This article will go through how to create a private docker registry. Docker registries provide a central location to store and distribute images. By default, Docker will use the Docker Hub, which is a public registry containing many Docker images. However, if you are using Docker a lot, and have images that you have created, then you likely have a need for a private registry.
We will begin by looking at how to deploy a simple private registry, for use in a non-production environment.
Deploying a Local Private Docker Registry
Docker have made a registry container image available, specifically for the purpose of running a docker registry. We can run it on a Docker host by running:
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
Deploying a Local Private Registry with a Volume
The data in the registry is stored locally on the docker host by default in a docker volume. We can see where this is mounted by running:
docker inspect registry | grep vol "Type": "volume", "Source": "/var/lib/docker/volumes/08660af98178690b97a3bbc35d4b7fa6a5df81ce60267abf484efc20b49fdba5/_data",
The source attribute shows where the storage is on the docker host. Rather than taking the default configuration we can specify a location for the container’s data when launch the registry container by using the -v or –volume flag:
$ docker run -d \ -p 5000:5000 \ --restart=always \ --name registry \ -v /mnt/registry data :/var/lib/registry \ registry:2
The addition of the -v flag to the docker run command will mount the registry data to /mnt/registrydata on the docker host.
Push an Image to a Local Docker Registry
Now, also on my Docker host, I already have the nginx image:
REPOSITORY TAG IMAGE ID CREATED SIZE registry 2 2d4f4b5309b1 2 weeks ago 26.2MB nginx latest 2622e6cca7eb 3 weeks ago 132MB
Now, I will add a new tag to the nginx image. By tagging it with the hostname and port of my private registry, Docker will know where to send it when pushing the image:
$ docker tag nginx:latest localhost:5000/nginx
Now when running docker images we can see the two entries for nginx, though note that they have the same image ID:
REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 2622e6cca7eb 3 weeks ago 132MB localhost:5000/nginx latest 2622e6cca7eb 3 weeks ago 132MB
Now, we can push the image to the private repository (localhost:5000) by running:
$ docker push localhost:5000/
nginx
Pulling an Image from a Local Private Registry
To test this works, I’m going to remove the two nginx images from my docker host:
$ docker rmi nginx:latest
$ docker rmi localhost:5000/nginx
Now we can pull the nginx image, but this time from the private registry rather than from docker hub:
$ docker pull localhost:5000/nginx
Stopping a Local Private Registry
You can stop the private registry container using:
$ docker container stop registry
Note that as we specified the –restart=always flag when we launched the container, it will start again automatically if the Docker host restarts.
Securing Remove Access to a Private Registry
So far, we have created and interacted with a private docker registry from the docker host on which the registry container is running. This has limited use, as it’s only available from that docker node. To make the registry accessible to other docker hosts we need to implement TLS to secure the transport between the docker host and the registry.
Creating a Self Signed Certificate
For this example I will create a self signed certificate. Note that this is just for a test environment. In production you would want to use a trusted certificate. First of all, create a directory where the certificate can be stored:
mkdir /certs
Next, the certificate can be created using openssl:
openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout /certs/domain.key \
-x509 -days 365 -out /certs/domain.crt
You will need to fill out the details requested, including the hostname of the server the certificate is for. I used registry.test.local as the server name for my certificate, and added a DNS entry on my DNS server pointing to the docker host where I was running the container.
Stop the registry container (if you have one running):
$ docker stop registry
Now we can start a registry container, but this time directing it to use the certificate just generated:
docker run -d \
--restart=always \
--name registry \
-v /certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 443:443 \
registry:2
Testing the Remote Private Registry
From another docker host, you can test the connectivity to the private registry:
$ curl -k registry.test.local:443
Because we have used a self signed certificate we need to tell docker to ignore insecure registries. To do so, add the following to the /etc/docker/daemon.json file (you may need to create the file if it doesn’t already exist!):
{
"insecure-registries" : ["myregistrydomain.com:5000"]
}
After making the change, restart docker:
$ systemctl restart docker
Note: you should never do this in a production environment as it would be a security risk, this is only for testing. In production you should be using a fully trusted certificate for your private registry.
Now we can download a new image, then tag it and push it to the private repository:
$ docker pull alpine:latest $ docker tag alpine:latest registry.test.local/myalpine $ docker push registry.test.local/myalpine
You should see your image get pushed to the private repository.
Enabling Authentication
To enable authentication for the remote registry we need to create a password file:
$ mkdir auth $ docker run \ --entrypoint htpasswd \ registry:2 -Bbn testuser testpassword > auth/htpasswd
Next stop the registry:
$ docker container stop registry
Now we can start the registry with authentication enabled:
$ docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v "$(pwd)"/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
Now, before we can interact with the registry we will need to log into it:
$ docker login registry.test.local:5000
Final Thoughts
Hopefully this has helped you understanding of how to create a docker private registry. We’ve looked at how to create a local and remote registry, how to configure registry storage and how to enable certificates and authentication. For more detail on all of these check out the link below to the official docker documentation on private registries.