I wanted to see how hard it was to deploy my own docker registry. It's not that hard and we have the benefit of living in our own little eco system. Here's the final script to deploy a private registry server on a stock debian/Ubuntu instance. Lets walk through it.

Deploy the registry container

The idea is that you copy this script over to the remote machine and run it. We setup a couple of things

  1. First check to see if docker is installed, and install it if not.
1
2
3
4
5
6
7
8
9
  #!/bin/bash

  HOST=registry.willschenk.com
  EMAIL=wschenk@gmail.com
  
  echo Checking to see if docker is installed
  if ! docker -v ; then
      curl -fsSL https://get.docker.com | sh
  fi

Caddy

We are going to use caddy-docker-proxy as our main webserver. This will watch the host system for docker containers that contain the caddy label, and automatically configure Caddy to serve them up. We also get https out of this so that's cool.

  1. Create the network caddy if it doesn't exist
  2. Create caddy_data and caddy_config so certs and other things survive containers
  3. Finally, start up the caddy container itself.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  ####
  # Caddy

  echo Checking for caddy network
  if [[ -z $(docker network ls | grep caddy) ]]; then
      echo -n Creating
      docker network create caddy
  fi

  echo Checking for caddy_data volume
  if [[ -z "$(docker volume ls | grep caddy_data)" ]]; then
      echo -n Creating
      docker volume create caddy_data
  fi

  echo Checking for caddy_config volume
  if [[ -z "$(docker volume ls | grep caddy_config)" ]]; then
      echo -n Creating
      docker volume create caddy_config
  fi

  docker pull lucaslorentz/caddy-docker-proxy:ci-alpine

  echo Stopping caddy if already started
  docker stop caddy && docker rm caddy

  echo Starting caddy
  docker run \
         --detach \
         --name caddy \
         --network caddy \
         --publish 80:80 \
         --publish 443:443 \
         --publish 443:443/udp \
         --label caddy.email=${EMAIL} \
         --env CADDY_INGRESS_NETWORKS=caddy \
         --volume /var/run/docker.sock:/var/run/docker.sock \
         --volume caddy_data:/data \
         --volume caddy_config:/config \
         lucaslorentz/caddy-docker-proxy:ci-alpine

Registry container

Now on to the registry itself.

  1. registry_data is where the images get stored
  2. registry_auth is where the username/passwords live
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  ##### Registry
  # Create the data volume
  # This is where the push images will live

  echo Checking for registry_data volume
  if [[ -z "$(docker volume ls | grep registry_data)" ]]; then
      echo -n Creating
      docker volume create registry_data
  fi

  echo Checking for registry_auth volume
  if [[ -z "$(docker volume ls | grep registry_auth)" ]]; then
      echo -n Creating
      docker volume create registry_auth
  fi

  ####
  # Registry user file
  # Create a htpasswd file in registry_auth container
  # This will get mounted in the registry container later
  # Needs to use -B for bcrypt

  docker run \
         --rm \
         --volume registry_auth:/auth \
         httpd:2 htpasswd -Bcb /auth/htpasswd registry-user password

  docker pull registry:latest

  echo Removing the old registry if started
  docker stop registry && docker rm registry

  echo Creating the registry container
  docker run \
         --detach \
         --name registry \
         --volume registry_data:/registry_data \
         --volume registry_auth:/auth \
         --network caddy \
         --label caddy=${HOST} \
         --label caddy.reverse_proxy='/v2/* {{upstreams 5000}}' \
         --env REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/registry_data \
         --env REGISTRY_AUTH=htpasswd \
         --env "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
         --env REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
         --publish 5000:5000 \
         registry

Copy and run

1
2
  scp registry.sh root@registry.willschenk.com:/root && \
      ssh root@registry.willschenk.com bash /root/registry.sh

Basic test

1
curl https://registry.willschenk.com/v2/_catalog | jq
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "errors": [
    {
      "code": "UNAUTHORIZED",
      "message": "authentication required",
      "detail": [
        {
          "Type": "registry",
          "Class": "",
          "Name": "catalog",
          "Action": "*"
        }
      ]
    }
  ]
}

OK, so lets try and log in

1
docker login registry.willschenk.com -u registry-user -p password
1
Login Succeeded

Then:

1
curl -u registry-user:password https://registry.willschenk.com/v2/_catalog | jq
1
2
3
4
{
  "repositories": [
  ]
}

Tag and push

Lets first log into the server and see what's in the registry_data volume.

Server:

1
2
3
4
  root@apple:~# docker volume inspect registry_data | awk '/Mountpoint/ {print $2}'
  "/var/lib/docker/volumes/registry_data/_data",
  root@apple:~# du -sh /var/lib/docker/volumes/registry_data/_data
  4.0K	/var/lib/docker/volumes/registry_data/_data

Now lets try to actually push something there. We'll pull down hello-world, tag it with the new name registry.willschenk.com/hello-world and then push it.

Client:

1
2
3
  docker pull hello-world
  docker tag hello-world:latest registry.willschenk.com/hello-world
  docker push registry.willschenk.com/hello-world

Now back on the server:

1
2
root@apple:~# du -sh /var/lib/docker/volumes/registry_data/_data
148K	/var/lib/docker/volumes/registry_data/_data

We've gone from 4.0K to 148K so something happened!

And if we check over the api itself:

1
curl -u registry-user:password https://registry.willschenk.com/v2/_catalog | jq
1
2
3
4
5
{
  "repositories": [
    "hello-world"
  ]
}

Notes

  • Right now registry.willschenk.com specified at the top of the file
  • Also you need to change the email address
  • Probably want to update that user/password combo

Previously

Style your RSS feed

2023-06-20

Next

Using caddy docker proxy straight from the dockerfiles

2023-06-22

labnotes

Previously

Using the cloudflare API setting A records ez

2023-06-20

Next

Installing old ruby on Ventura back in time

2023-06-28