More recently I have found myself moving more towards the self hosted mentality. As a result, I have decided that having my own docker registry would be quite useful to avoid changes happening without my explicit consent.

If you haven’t seen the wonders of containers yet, I can highly recommend their usage. If, like me, you like to test things out like applications or services etc, then containers are probably for you. In the past I have struggled to keep my systems clean as a multitude of legacy files and applications clutter up my system. With containers, that goes away. I can fire up a wordpress container (and I have done recently), play with it, realise I still hate it, tear it down, and throw it away…all within 30 minutes. All this without a trace of it’s existence, allowing me to deny that I ever tried it again.

If you’re still not on board, I would recommend just trying it out. Start at https://www.docker.com/ and see what all the fuss is about. Hopefully, over the coming months, I will include some of my favourite self hosted projects on this blog.

Prerequisites:

  • Docker
  • Docker Compose

I’m using linux for my setup, but this will work on other systems too. However, all of my instructions will be for linux

Getting started

I will show you my setup and then I will explain what I’m doing with each piece so that you can apply it to your own setup

On my server I have a docker directory where I create my docker projects or where I pull the git repos, of docker projects, to. Open a terminal inside that directory and create new directory called docker-registry and cd into it:

$ mkdir docker-registry $ cd docker-registry

Create a new file called docker-compose.yml and edit it using your preferred text editor

$ vi docker-compose.yml

Paste the following into the file:

    version: '3.3'
    services:
      registry:
      container_name: registry
      image: registry:2
        ports:
          - '5000:5000'
        restart: unless-stopped 
      volumes:
        - <enter local directory e.g. /mnt/DockerData/docker-registry>:/var/lib/registry

Breakdown of the compose file:

version: '3.3'

The version of compose to use

    services:
      registry:

This defines the service

container_name: registry

What we should name the container

image: registry:2

The image that we will pull from docker hub

        ports:
            '5000:5000'

The port to expose. First is the local port on your host system. Second is the port that it will forward to in your container

restart: unless-stopped

This will restart the service if the host restarts or if there is an issue that makes the service stop. It will not restart if you stop it manually

      volumes:
            - /mnt/DockerData/docker-registry:/var/lib/registry

This is where the data will be stored on our host system. This means that you won’t lose the data if you recreate the container

Quit out of your editor and run the compose command to start it up:

$ docker-compose up -d

Pull (or build) some image from the hub:

$ docker pull ubuntu:16.04

Tag the image so that it points to your registry:

$ docker tag ubuntu:16.04 localhost:5000/my-ubuntu

Push it to your local registry:

$ docker push localhost:5000/my-ubuntu

Remove the locally-cached ubuntu:16.04 and localhost:5000/my-ubuntu images, so that you can test pulling the image from your registry. This does not remove the localhost:5000/my-ubuntu image from your registry:

$ docker image remove ubuntu:16.04 $ docker image remove localhost:5000/my-ubuntu

Pull the localhost:5000/my-ubuntu image from your local registry:

$ docker pull localhost:5000/my-ubuntu

If all you’re after is a local docker registry, you can stop here. If you want SSL and access from outside the network, you will need to continue.

Exposing over SSL

There are a few ways to expose this over SSL, I have decided to leave my config alone and just use nginx to forward requests. I’ve started using containers to host mine. I will most probably go through the rest of my setup later, however, all you need at this point is an nginx server and a letsencrypt certificate for the host domain. I’ve covered both of these topics in the past so I’ll just detail my nginx setup here:

    server {
            listen   80;
            server_name <example.com>;

            location / {
                    return 301 https://$host$request_uri;
            }
            location ~ /.well-known {
                    allow all;
            }
    }

    server {
            listen 443 ssl;
            server_name <example.com>;
            ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
            ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
            ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
            ssl_ciphers         HIGH:!aNULL:!MD5;

            location / {
                    proxy_pass http://example.com:5000;
            }

    }

This will take your requests and forward them straight to your registry. You can test here using the same commands as above, replacing the domain with yours. Otherwise, you can add some basic authentication by following the next section.

Authentication setup

Firstly we need to create an htpasswd file, inside the same directory as your compose file, run the following:

    mkdir auth
    docker run --entrypoint htpasswd registry:2 -Bbn <insert your user> <insert your password> > auth/htpasswd

Then stop the registry:

    docker container stop registry

Open the docker-compose.yml file and add the extra volume and environment details to make the file look like the following:

    version: '3.3'
    services:
      registry:
      container_name: registry
      image: registry:2
        ports:
          - '5000:5000'
        restart: unless-stopped 
      volumes:
        - /mnt/DockerData/docker-registry>:/var/lib/registry
        - ./auth:/auth
      environment:
        - REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
        - REGISTRY_AUTH=htpasswd
        - REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm

Restart your registry via docker:

docker-compose up -d

Now you will need to login to the registry before being able to do anything with it:

    docker login example.com

Before we test it, we’ll need to remove the image we created earlier (if it still exists):

docker image remove localhost:5000/my-ubuntu

Pull (or build) some image from the hub:

$ docker pull ubuntu:16.04

Tag the image so that it points to your registry:

$ docker tag ubuntu:16.04 example.com/my-ubuntu

Push it to your local registry:

$ docker push example.com/my-ubuntu

Remove the locally-cached ubuntu:16.04 and example.com/my-ubuntu images, so that you can test pulling the image from your registry. This does not remove the example.com/my-ubuntu image from your registry:

$ docker image remove ubuntu:16.04 $ docker image remove example.com/my-ubuntu

Pull the example.com/my-ubuntu image from your local registry:

$ docker pull example.com/my-ubuntu

All done.