weekends are for leisure

Running a local container registry

Tags: #Docker  #Linux  #Debian  #Kubernetes 

In the spirit of learning new things, I figured it’d be nice to have a local container registry for personal projects so I don’t have to fuss with Docker Hub or other. It also pairs nicely with the bare-metal Kubernetes cluster I’m building. Let’s set one up!

I originally learned how to create a local registry from FreeCodeCamp’s great How to Self-host a Container Registry article. I’m sharing my setup here because it differs slightly:

Network preparation

Before changing my server configuration, I first needed to configure my home network to recognize registry.home.arpa. So I edited my dnsmasq configuration file and added a static DNS record for registry.home.arpa pointing to my server’s IP address.

Server configuration

My main home server is a Debian machine that already runs docker. And thankfully, Docker (the company) publishes a registry container for running your own local container registry. Geez that’s a mouthful …

First, I created a standalone user and group.

1
2
3
groupadd -g 2012 registry
useradd -m -g registry -s /bin/bash -u 2012 registry
usermod -aG docker registry

Then, as the new registry user, I created some necessary directories.

1
2
cd
mkdir auth data

I used the following Docker Compose YAML, with the user parameter, and numeric user+group ids, so it runs unprivileged.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Save as /home/registry/docker-compose.yaml
services:
  registry:
    user: 2012:2012
    image: registry:latest
    environment:
      REGISTRY_AUTH: htpasswd
      REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
      REGISTRY_AUTH_HTPASSWD_PATH: /auth/registry.password
      REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
    volumes:
      - ./auth/registry.password:/auth/registry.password
      - ./data:/data
    ports:
      - 5000

I’m using htpasswd as the REGISTRY_AUTH option, so I used the following command to create the password file.

1
htpasswd -cbB auth/registry.password reg_user reg_pass

Then, I generated a self-signed TLS certificate.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# generate self-signed certs
DEST="ssl"
mkdir $DEST

for domain in registry.home.arpa; do
    echo "Generating cert for ${domain}"
    
    openssl req -nodes -x509 -newkey rsa:2048 \
        -keyout ${DEST}/${domain}.key \
        -out ${DEST}/${domain}.crt \
        -days 365 \
        -subj "/C=US/ST=Massachussett/L=Boston/O=Home/CN=${domain}"  \
        -addext "subjectAltName = DNS:${domain}" 
done

Next, I copied the certs to a location accessible by Caddy, and updated my Caddyfile with the following site configuration. I use Caddy as a webserver whenever I can.

1
2
3
4
5
https://registry.home.arpa {
    tls ssl/registry.home.arpa.crt ssl/registry.home.arpa.key
    log
    reverse_proxy :5000
}

After that, I copied the registry.home.arpa.crt file to every Debian server that will be talking to the registry. Then I moved the file into the right folder and ran a command to have Linux recognize it.

1
2
3
# As root
cp *.crt /usr/local/share/ca-certificates/
update-ca-certificates

On my docker machine, I ran systemctl restart docker to pick up the new self-signed cert.

With the cert in place, I once again changed to my registry user and started the registry.

1
docker compose up

Finally, I restarted Caddy to enable the new site.

Kick the tires

You should hopefully have a working registry now. Try logging in as a test.

1
docker login https://registry.home.arpa

If that works, you should be all set. Push, pull, be happy.



Older Post