An Enthusiastic Programmer

Secure your registry with SSL certificate


In the previous article, we do achieve access registry from other hosts, but it’s insecure. The data transform between server and client can be sniffed or modified by malicious hackers. So, In this article, I will show you the first step for securing is that use an SLL certificate for production.


  • A running Docker registry.
  • A domain name or subdomain name.

Get Certificate

There are a couple of ways that you can get a certificate. You can buy from the certification authority(CA) or obtain it from free websites(Let’s Encrypt, SSL For Free…).

Here, I generated a certificate for my domain from Let’s Encrypt command shell. Let’s Encrypt command shell is convenient for generating public certificates. With a few steps, you’ll get your certificate. Once you finish, you’ll have the following files under /etc/letsencrypt/live/

$ pwd
$ ls
cert.pem  chain.pem  fullchain.pem  privkey.pem  README

You’ll get five files as the above, open the README file. And it’ll tell you each file’s details. Here, the fullchain.pem is a certificate file, and privkey.pem is a private key file, and when you use them, you don’t need to change the file name, just use it directly.

NOTE: Generate an SSL certificate from an IP address is possible, but it's rarely used. Certificates attached to IP address faces revoking risk, and it also doesn't have any help for DNS cache. For more see Is it possible to have ssl certificate for ip address not domain?.

Run your registry up with SSL certificate

There are plenty of ways you can set your SSL certificate over your registry connection. You can use Ngnix, Composefile ect.

In the following example, I’ll use the docker run command with REGISTRY_HTTP_TLS_CERTIFICATE and REGISTRY_HTTP_TLS_KEY variable to set up an SSL connection.

docker run -d \
  --restart=always \
  --name registry \
  -v /etc/letsencrypt:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/live/your.domain/fullchain.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/live/your.domain/privkey.pem \
  -p port_host:port_container \

Replace the your.domain with your domain, and the port_host with your host’s port, and port_container with your container’s port. You can specify your mount path through -v variable. In the above mount example, you should make sure your certificate is under the path /etc/letsencrypt/live/your.domain in your hostmachine.

The following is my whole code snippet.

docker run -d \
  --restart=always \
  --name registry \
  -v /etc/letsencrypt:/certs \
  -e REGISTRY_HTTP_TLS_KEY=/certs/live/ \
  -p 5050:5000 \

Once your registry is running up, you can access your registry from other hosts with a secure connection over SSL. In the previous article, we’ve discussed how to add insecure connection to a docker daemon. Here, As you are using SSL protected, so it is a secure connection, and hence you don’t need to add the registry address on the client docker daemon in the insecure connection configuration.

As you can see from the above, I mounted the path /etc/letsencrypt in local machine to /certs in the container. Don’t mount the path that includes dots, which may cause unexpected errors.

The following command got failed on my machine.

$ docker run \
  --restart=always \
  --name 2registry \
  -v /etc/letsencrypt/live/ \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/fullchain.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/privkey.pem \
  -p 5051:5000 \
time="2020-09-28T05:49:36.509990307Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.11.2 service=registry version=v2.7.1 
time="2020-09-28T05:49:36.511286456Z" level=info msg="redis not configured" go.version=go1.11.2 service=registry version=v2.7.1 
time="2020-09-28T05:49:36.510525597Z" level=info msg="Starting upload purge in 12m0s" go.version=go1.11.2 service=registry version=v2.7.1 
time="2020-09-28T05:49:36.526651206Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.11.2 service=registry version=v2.7.1 
time="2020-09-28T05:49:36.527667014Z" level=fatal msg="open /certs/fullchain.pem: no such file or directory"

This is because that the docker isn’t very compatible with dots. Hope docker will fix this bug in the future. You can check whether your docker support dots. But I highly recommended that don’t use dots or any other special character in your mounted path.

SSL Checker is a great tool for checking is if a website has an SSL certificate on. If you follow the above steps strictly, then you’ll see all green right symbols on the SSL Checker(There should not be any break symbols). Just as the following image. protected with SSL

Once your Docker registry is running up, you can access your registry with secure connection from other hosts as the following snippet:

$ docker pull redis
$ docker tag redis
$ docker push