Home DevOps Create a Private Docker Registry – Tutorial

Create a Private Docker Registry – Tutorial

by admin
docker-private-registry

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.

https://docs.docker.com/registry/deploying/

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More