A Docker registry is a storage and content delivery system that holds named Docker images, available in different tagged versions. Users using Docker interact with a registry by using docker push and docker pull commands. Sometimes it makes sense to store Docker images on a local registry rather than pushing them to Docker hub. In this guide we will setup Private Docker Registry on CentOS 7 secured with Let’s Encrypt SSL Certificate.

You’ll save a lot of bandwidth for a big team and keep the images that you don’t want to be exposed to the public safe. Creating a local docker registry on CentOS 7 is a matter of following few steps. For installation of Docker on different distributions refer to How to install Docker CE on Linux Systems.

Step 1: Install Docker CE on CentOS 7

We’ll use registry 2 Docker image to create a running instance of Docker registry on CentOS 7.

Update your CentOS 7 system:

sudo yum -y update

Run the commands below to install Docker CE on CentOS 7.

sudo curl -o /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo 

Then install Docker CE packages:

sudo yum -y install docker-ce docker-ce-cli containerd.io

If you would like to use Docker as a non-root user, you should now consider adding your user to the “docker” group:

sudo usermod -aG docker $USER
newgrp docker

Run the command below to see a version of docker installed.

$ docker --version
Docker version 20.10.6, build 370c289

Start and enable docker service:

sudo systemctl enable --now docker

Status should be running:

 $ systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2021-04-29 19:22:34 UTC; 17s ago
     Docs: https://docs.docker.com
 Main PID: 5243 (dockerd)
    Tasks: 8
   Memory: 43.4M
   CGroup: /system.slice/docker.service
           └─5243 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Step 2: Generate Let’s Encrypt SSL Certificates

As promised earlier we’ll use Let’s Encrypt SSL Certificates to secure our Docker Registry setup running on CentOS 7.

Install certbot tool that will be used to request for Let’s Encrypt certificate.

sudo yum -y install epel-release
sudo yum -y install certbot

Request for SSL certificate:

export DOMAIN="registry.domain.com"
export EMAIL="[email protected]"
certbot certonly --standalone -d $DOMAIN --preferred-challenges http --agree-tos -n -m $EMAIL --keep-until-expiring

Expected certificates generation output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for registry.computingforgeeks.com
Waiting for verification...
Cleaning up challenges

 - Congratulations! Your certificate and chain have been saved at:
   Your key file has been saved at:
   Your cert will expire on 2021-07-28. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Your certs will be saved under /etc/letsencrypt/live/



  • fullchain.pem ⇒ combined file cert.pem and chain.pem
  • chain.pem ⇒ intermediate certificate
  • cert.pem ⇒ SSL Server cert(includes public-key)
  • privkey.pem ⇒ private-key file

Step 3: Run Docker registry on CentOS 7

You can either run docker registry with SSL or without. We’ll demonstrate how you can run Docker registry with either of the methods.

But first, create a directory that will hold Docker registry images:

sudo mkdir /var/lib/docker/registry

Run Local Docker registry without SSL

$ docker run -d -p 5000:5000 \
--name docker-registry \
--restart=always \
-v /var/lib/docker/registry:/var/lib/registry \

Run Local Docker registry with SSL

Create directory and place certs on the host:

export DOMAIN="registry.domain.com"
mkdir /certs
sudo cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem /certs/fullchain.pem
sudo cp /etc/letsencrypt/live/$DOMAIN/privkey.pem /certs/privkey.pem
sudo cp /etc/letsencrypt/live/$DOMAIN/cert.pem /certs/cert.pem

Create a Docker Registry container:

docker run -d --name docker-registry --restart=always \
-p 5000:5000 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/fullchain.pem  \
-e REGISTRY_HTTP_TLS_KEY=/certs/privkey.pem \
-v /certs:/certs \
-v /var/lib/docker/registry:/var/lib/registry \

Replace registry.domain.com with your registry subdomain name.

It will download registry:2 docker image if it doesn’t exist and create a container

Unable to find image 'registry:2' locally
2: Pulling from library/registry
ddad3d7c1e96: Pull complete
6eda6749503f: Pull complete
363ab70c2143: Pull complete
5b94580856e6: Pull complete
12008541203a: Pull complete
Digest: sha256:bac2d7050dc4826516650267fe7dc6627e9e11ad653daca0641437abdf18df27
Status: Downloaded newer image for registry:2

Check container state:

# docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                                       NAMES
ed960bd27529   registry:2   "/entrypoint.sh /etc…"   28 seconds ago   Up 27 seconds>5000/tcp, :::5000->5000/tcp   docker-registry

To push images to Registry Container server, set like below:

$ curl https://$DOMAIN:5000/v2/_catalog

If you have SELinux enabled, you may encounter a problem using port 5000, consider disabling SELinux or putting it on permissive mode if you get issues.

If firewalld is enabled and running, allow the port on the firewall.

sudo firewall-cmd --add-port=5000/tcp --permanent
sudo firewall-cmd --reload

Step 4: Add Insecure Registry to Docker Engine

By default, docker uses https to connect to docker registry. But there can be use cases to use an insecure registry, especially if you’re on a trusted network. This eliminates the need for a CA-signed certificate for internal use or to trust self-signed certificate in all docker nodes. Here are the steps to add Insecure Registry to Docker Engine.

For Ubuntu Xenial, edit /etc/docker/daemon.json and update the key “insecure-registries”. e.g.

 "insecure-registries" : ["registry.domain.com:5000"]

For CentOS 7, edit the file /etc/docker/daemon.json, e.g.

 "insecure-registries" : ["registry.domain.com:5000"]

For Ubuntu trusty, edit the file /etc/default/docker and update DOCKER_OPTS, e.g

DOCKER_OPTS='--insecure-registry registry.domain.com:5000'

Then restart Docker engine

sudo systemctl restart docker

Step 5: Pushing Docker images to the local registry

Now that the registry is ready, you can start pushing docker images to it. If you don’t have an active DNS server, use /etc/hosts file to map the hostname to IP Address.

$ sudo vim /etc/hosts registry.domain.com

I’ll download ubuntu latest docker image from Docker hub and push it to my local Docker registry.

$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
345e3491a907: Pull complete
57671312ef6f: Pull complete
5e9250ddb7d0: Pull complete
Digest: sha256:cf31af331f38d1d7158470e095b132acd126a7180a54f263d386da88eb681d93
Status: Downloaded newer image for ubuntu:latest

Tag the image as registry.domain.com:5000/ubuntu. This creates an additional tag for the existing image. When the first part of the tag is a hostname and port, Docker interprets this as the location of a registry, when pushing.

$ docker tag ubuntu $DOMAIN:5000/ubuntu

Push the image to the local registry running at registry.domain.com:5000/ubuntu

$ docker push $DOMAIN:5000/ubuntu
Using default tag: latest
The push refers to repository [registry.domain.com:5000/ubuntu]
2f140462f3bc: Pushed
63c99163f472: Pushed
ccdbb80308cc: Pushed
latest: digest: sha256:86ac87f73641c920fb42cc9612d4fb57b5626b56ea2a19b894d0673fd5b4f2e9 size: 943

If the image upload was successful, you should get sha256 hash at the end. Pushed images are stored under /var/lib/registry/docker/registry/v2/repositories directory.

$ ls /var/lib/docker/registry/docker/registry/v2/repositories/

This is the same method you’ll use to push custom docker images. To download docker images on the local registry, use the command:

$ docker pull registry-hostname:500/image:tag

$ docker pull registry.domain.com:5000/ubuntu

On my next guide, I’ll cover configuring nginx proxy for accessing the repository over https.

You can also take a look at Install Docker UI manager – Portainer

Your support is our everlasting motivation,
that cup of coffee is what keeps us going!

As we continue to grow, we would wish to reach and impact more people who visit and take advantage of the guides we have on our blog. This is a big task for us and we are so far extremely grateful for the kind people who have shown amazing support for our work over the time we have been online.

Thank You for your support as we work to give you the best of guides and articles. Click below to buy us a coffee.


Please enter your comment!
Please enter your name here