How to Setup Secure Docker Private Registry on Ubuntu 24.04

In this tutorial, we are going to learn how to setup secure Docker private registry on Ubuntu 24.04. We will cover everything from generating SSL certificates with Subject Alternative Names (SANs) to configuring Nginx as a reverse proxy within a Docker Compose setup, and finally, securing your registry with basic authentication. By the end of this tutorial, you’ll have a fully functional and secure private registry ready for your development and deployment workflows

Why Private Docker Registry ?

For Smooth CI/CD development using the docker platform, consider using a self-hosted docker registry server. Docker registry is the repository where you can store your docker images and pull them to run applications on the server. For faster delivery as well as secure infrastructure, it is recommended to set up your own docker private registry to store your docker images and distribute among organizations.

Prerequisites

  • Two Linux Servers (Ubuntu 24.04): You will need one server to host your Docker Registry (the registry host) and another to act as a client for pushing and pulling images. These can be virtual machines or cloud instances.
  • User account with sudo privileges
  • Private Registry FQDN: registry.linuxtechi.org
  • Pre install Docker and Docker-Compose on both servers.

What is Docker Registry?

Docker Registry is a Server-side application which allows you to store your docker images locally into one centralized location. By setting up your own docker registry server, you can pull and push docker images without having to connect to the Docker hub, saving your bandwidth and preventing you from security threats.

Before You start

Before starting, I ensure that you have installed Docker and Docker-Compose on both client server and local registry server. To verify you have installed required software, you can run the following commands to check the software version.

docker version
docker-compose version

Docker And Docker-Compose Version Check

Also, you need to ensure that docker service is started and is setup to enable at boot time:

sudo systemctl start docker
sudo systemctl enable docker

Steps to Setup Secure Docker Private Registry

This section outlines the detailed steps to set up your secure Docker private registry. We will primarily work on our registry host server.

1) Generate SSL Certificates with Subject Alternative Names (SANs)

Modern browsers and Docker clients require SSL certificates to include Subject Alternative Names (SANs) rather than relying solely on the Common Name (CN) field. This is crucial to avoid errors like ‘x509: certificate relies on legacy Common Name field, use SANs instead‘. We will generate self-signed certificates for this tutorial. For production environments, it is highly recommended to use certificates from a trusted Certificate Authority (CA) like Let’s Encrypt.

First, create a directory to store your certificates and navigate into it:

mkdir -p ~/private-registry/certs
cd ~/private-registry/certs

Next, create an OpenSSL configuration file named ‘openssl.cnf‘. This file will define the certificate details, including the SANs. Replace ‘registry.linuxtechi.org‘ with your actual FQDN.

vi openssl.cnf
Add the following content
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C = IN
ST = New Delhi
L = New Delhi
O = LinuxTechi
OU = IT Department
CN = registry.linuxtechi.org

[v3_req]
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = registry.linuxtechi.org
Save and close the file. Now, generate your private key and Certificate Signing Request (CSR) using this configuration file:
openssl genrsa -out registry.linuxtechi.org.key 2048
openssl req -new -key registry.linuxtechi.org.key -out registry.linuxtechi.org.csr -config openssl.cnf
Finally, sign your certificate using the private key and the configuration file. This command creates your self-signed certificate with the specified SANs:
openssl x509 -req -days 365 -in registry.linuxtechi.org.csr -signkey registry.linuxtechi.org.key -out registry.linuxtechi.org.crt -extensions v3_req -extfile openssl.cnf
After executing these commands, you will have `registry.linuxtechi.org.key` (your private key) and `registry.linuxtechi.org.crt` (your self-signed certificate with SANs) in the `~/private-registry/certs` directory. These files are essential for securing your Nginx reverse proxy.
Docker Private Registry Certificates Ubuntu 24.04

2) Prepare Your Docker Compose File with Registry and Nginx

You need to create a new docker-compose.yml script that defines the docker-compose version and services required to set up a private registry.

Instead of running Nginx as a system daemon, we will containerize it within our Docker Compose setup. This approach offers better portability and easier management. First, create the necessary directory structure for Nginx configuration:

mkdir -p ~/private-registry/nginx/conf.d

Now, navigate to your ~/private-registry directory and create a docker-compose.yml file. This file will define both your Docker Registry service and your Nginx reverse proxy service.

cd ~/private-registry
vi docker-compose.yml

Add the following content to docker-compose.yml file:

services:
  registry:
    image: registry:latest
    ports:
      - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /registry-data
    volumes:
      - ./auth:/auth
      - ./registry-data:/registry-data
    restart: always

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./certs:/etc/nginx/ssl
    depends_on:
      - registry
    restart: always

Save and close the file

Let’s break down this docker-compose.yml configuration:

registry service:

  • image: registry:latest: Pulls the official Docker Registry image.
  • ports:”5000:5000″: Maps the container’s port 5000 to the host’s port 5000. While Nginx will handle external access, this mapping can be useful for debugging.
  • environment: Configures the registry for basic authentication using htpasswd.
  • volumes: Persists authentication data and registry images on the host machine, ensuring data is not lost when the container is restarted.

nginx service:

  • image: nginx:latest: Pulls the official Nginx image.
  • ports: “80:80”, “443:443”: Exposes Nginx on the standard HTTP and HTTPS ports.
  • volumes: Mounts our custom Nginx configuration and the generated SSL certificates into the Nginx container.
  • depends_on: – registry: Ensures that the registry service is started before the Nginx service.

3) Configure Nginx Reverse Proxy

Now, we need to create the Nginx configuration file that will be mounted into our Nginx container. This file will instruct Nginx on how to proxy requests to our Docker registry and how to use the SSL certificates we generated.

Create a new file named default.conf in the ~/private-registry/nginx/conf.d

vi ~/private-registry/nginx/conf.d/default.conf

Add the following Nginx configuration to the default.conf file. Remember to replace registry.linuxtechi.org with your actual FQDN.

server {
    listen 80;
    server_name registry.linuxtechi.org;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name registry.linuxtechi.org;

    ssl_certificate /etc/nginx/ssl/registry.linuxtechi.org.crt;
    ssl_certificate_key /etc/nginx/ssl/registry.linuxtechi.org.key;

    # Disable SSLv3 to prevent POODLE attack
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

    client_max_body_size 0;
    chunked_transfer_encoding on;

    location / {
        proxy_pass http://registry:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 900;

        # Docker Registry specific headers
        proxy_set_header Authorization $http_authorization;
        proxy_pass_request_headers on;
        proxy_buffering off;
    }
}

save and exit the file. Now, start the containers using following commands.

cd ~/private-registry && docker compose up -d

This command will build and start both your registry and Nginx containers in detached mode. You should see output indicating that the services have been created and started successfully.

Verify the container status, run

docker ps

Setup Docker Private Registry on Ubuntu 24.04

4) Set Up Basic Authentication with htpasswd

To secure your registry and prevent unauthorized access, we will implement basic authentication using htpasswd. This ensures that only users with valid credentials can push and pull images. First, you need to install the apache2-utils package, which provides the htpasswd utility:
sudo apt install apache2-utils -y

Now, create your first user. Replace devops with your desired username. You will be prompted to enter and confirm a password.

sudo htpasswd -Bc ~/private-registry/auth/registry.password devops

The -B flag forces bcrypt encryption for the password, and the -c flag creates the password file. For subsequent users, omit the -c flag to append to the existing file:

5) Configure Docker Client to Trust Your Registry

First, copy your self-signed SSL certificate (registry.linuxtechi.org.crt) from your registry host to your client machine. You can use scp or any other secure file transfer method. Then, create a directory for your registry’s certificate and copy the certificate into it:

sudo mkdir -p /etc/docker/certs.d/registry.linuxtechi.org:443
sudo cp registry.linuxtechi.org.crt /etc/docker/certs.d/registry.linuxtechi.org:443/ca.crt

Now, restart the Docker daemon for the changes to take effect:

sudo systemctl daemon-reload && sudo systemctl restart docker

6) Test Your Private Registry

Now it’s time to test your secure private registry. On your client machine, log in to your registry using the username and password you created in Step 4:

docker login registry.linuxtechi.org:443

Login To Secure Docker Private Registry Command line

You will be prompted for your username and password. If the login is successful, you will see a “Login Succeeded” message.
Next, let’s tag a local Docker image and push it to your private registry. Replace hello-world with any local image you have, and registry.linuxtechi.org/my-app:latest with your desired image name and tag.
docker tag hello-world registry.linuxtechi.org:443/devops/my-app:latest
docker push registry.linuxtechi.org:443/devops/my-app:latest
Docker Push Private Registry

Once push is completed, you can go to browser and enter the url:

https://registry.linuxtechi.org/v2/_catalog

Replace registry.linuxtechi.org with your own url and provide basic authentication. You will find repositories list as :

Docker Private Registry GUI Catalog

To verify that the image is stored correctly, let’s remove the local image and pull it back from your private registry:
docker rmi registry.linuxtechi.org:443/devops/my-app:latest
docker pull registry.linuxtechi.org:443/devops/my-app:latest

Docker Pull Image From Docker Private Registry

If the pull is successful, congratulations! You have successfully set up a secure Docker private registry.

That’s all from this tutorial, I hope you have found it useful and informative. Kindly do post your queries and feedback in below comments section.

Also Read : How to Install KVM on Ubuntu 24.04 Step-by-Step

2 thoughts on “How to Setup Secure Docker Private Registry on Ubuntu 24.04”

  1. Great guide but I found a small typo, the compose file has

    REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.passwd

    but then you create the htpasswd file as registry.password

    needs to be
    htpasswd -Bc registry.passwd linuxtechi

    docker log:
    msg=”error checking authorization: stat /auth/registry.passwd: no such file or directory”

  2. ERROR: The Compose file ‘./docker-compose.yml’ is invalid because:
    Unsupported config option for networks: ‘mynet’
    Unsupported config option for volumes: ‘myregistrydata’
    Unsupported config option for services: ‘registry’

    Not working 🙁

Leave a Comment

Your email address will not be published. Required fields are marked *