Create a Private Docker Registry

semirteskeredzic

Semir Teskeredzic

Posted on June 6, 2021

Create a Private Docker Registry

Dockerized applications and automated pipelines mean that you will eventually need a docker registry sooner or later.

What is a Docker Registry?

It is essentially a place where you store Docker images organized in repositories. You can push images to the remote Docker registry and pull images where you want them.

Docker Hub

Nifty service that Docker.com runs is Docker hub or a Docker Registry that has many things automated so you don't have to bother with configuring it on your own server (which we will get to in a bit).

Free tier is also fairly generous at Docker hub because it provides you with unlimited public repositories and one private repository. More private repositories are available in paid tiers and you can find pricing info here: Docker Pricing

Create your own private registry

If you want to house your Docker images on your own server, then you have to create a private registry or how the Docker documentation states it, deploy a registry server.

Prerequisites

Before deploying a private registry, you will need to have several prerequisites checked:

  • Server running Ubuntu (preferably latest Ubuntu 20.04) that is properly configured. You can read about it in the post here: Configure New Ubuntu Server
  • Docker & Docker-compose properly installed on the server that will host the Docker registry. You can read about it here: Install Docker & Docker-compose on Ubuntu server
  • Properly installed Nginx. You can read about it here: Install Nginx on Ubuntu server
  • Domain name that resolves to your host server (where you will host registries)
  • SSL Secured Nginx on your host server

Installing & Configuring

Since we have to run registry from a docker image there are two ways we will go through here,

  1. Quick - running with docker command from the CLI
  2. Using docker-compose - running with the yml file using docker-compose

Before going through each one, go on and create a few directories to keep the things organized:

$ mkdir docker-registry
$ cd ~/docker-registry
$ mkdir data
Enter fullscreen mode Exit fullscreen mode

Quick

While in our docker-registry folder, we can run docker registry with just one line of code:

$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
Enter fullscreen mode Exit fullscreen mode

This will run our registry from the image registry in background mode with the usage of the port 5000.
This is a great first step just to see it working. However it is not secure enough or configured enough for production-based environments.
Go on and stop the running container by running this command:

$ docker container stop registry
Enter fullscreen mode Exit fullscreen mode

Proceed with removing the container alltogether:

$ docker container rm -v registry
Enter fullscreen mode Exit fullscreen mode

If we want to store data to our host server filesystem instead of relying on the data inside the docker volume, we could use this command:

docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -v ~/docker-registry/data:/var/lib/registry \
  registry:2
Enter fullscreen mode Exit fullscreen mode

Note: when writing a docker command in multiple lines just use backslash \ at the end of the line and press enter.

Using docker-compose

You can write entire configuration in one yml file and run it with docker-compose, this way you see the configuration more clearly and you are able to make modifications and see previous configurations.
While in your docker-registry directory, create and enter edit mode using nano:

$ nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

You can now enter following code to the file:

version: '3'

services:
  registry:
    image: registry:2
    restart: always
    ports:
    - "5000:5000"
    environment:
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./data:/data
Enter fullscreen mode Exit fullscreen mode

You can already see that we are using same image, utilizing same port and using volume location ~/data in our filesystem.
You can exit and save using CTRL+X then Y and then ENTER
Run the docker-compose with:

$ docker-compose up
Enter fullscreen mode Exit fullscreen mode

Now your docker registry is up and running.

Configure HTTP Authentication

We can put HTTP Authentication with the Nginx using username and password so the access to our server hosting registries is secured with those credentials.
First update local packages:

$ sudo apt update
Enter fullscreen mode Exit fullscreen mode

Then install a package we will use:

$ sudo apt install apache2-utils -y
Enter fullscreen mode Exit fullscreen mode

We will store our credentials in separate folder so go create one and enter it:

$ mkdir ~/docker-registry/auth
$ cd ~/docker-registry/auth
Enter fullscreen mode Exit fullscreen mode

We will now enter following command and you replace username with the username you want it to be:

$ htpasswd -Bc registry.password username
Enter fullscreen mode Exit fullscreen mode

You will be prompted for password so enter it as required. If we used a docker-compose file, we need to update it to include credentials. Open docker-compose file using nano:

$ nano ~/docker-registry/docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

Now alter existing docker-compose using following template:

version: '3'

services:
  registry:
    image: registry:2
    restart: always
    ports:
    - "5000:5000"
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./auth:/auth
      - ./data:/data
Enter fullscreen mode Exit fullscreen mode

Run your docker-compose:

$ docker-compose up
Enter fullscreen mode Exit fullscreen mode

If you want to use inline CLI docker command use this:

docker run -d \
  -p 5000:5000 \
  --restart=always \
  --name registry \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM=Registry \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/registry.password \
  -v ~/docker-registry/data:/var/lib/registry \
  registry:2
Enter fullscreen mode Exit fullscreen mode

Secure your host server with SSL

In order for docker-registry to function properly you must have secured HTTPS connection and that requires that you install a certificate provided by Certificate Authority. There is a free SSL certificate provided by Let's Encrypt and you can do this with the help of certbot
Find the guide here: Certbot Ubuntu installation

Remember that you need to have a domain pointing to your host server IP address before being able to activate Let's encrypt.

Check firewall

Check the firewall status with the following command:

$ sudo ufw status
Enter fullscreen mode Exit fullscreen mode

And make sure to allow all Nginx that includes HTTPS:

$ sudo ufw allow 'Nginx Full'
Enter fullscreen mode Exit fullscreen mode

Nginx file Upload size

File upload size is set quite low for nginx when you freshly install and configure it. You need to increase that amount so you can push larger images to your docker registry. Open Nginx configuration:

$ sudo nano /etc/nginx/nginx.conf
Enter fullscreen mode Exit fullscreen mode

Now edit the line that says client_max_body_size in the http section and set the value to 8000m which sets the maximum size to 8GB. Save changes and exit configuration.
After making any changes in Nginx configuration be sure to restart the Nginx:

$ sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Congratulations! You now have a private docker registry that is password protected and HTTPS secured. You can push your own images there and pull it wherever you need them. Use docker commands to login to your docker-registry just as you would with Docker Hub, and use push and pull commands in the same way.

Thank you for reading.

💖 💪 🙅 🚩
semirteskeredzic
Semir Teskeredzic

Posted on June 6, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related