Adding a Local Docker Registry

You are viewing article number 2 of 3 in the series Self-Hosted GitHub Runners: Learn as I Go (LAIG)

In this post, we build on the work covered in previous post Local Self-Hosted Runners using Docker Compose, by adding a local docker registry to the stack; this will allow workflow jobs executing on our self-hosted runner to push/pull images to/from the registry.

Docker Compose Stack With Dkr Registry 1

Docker Daemon – Enable Insecure Registry

By default, connections to insecure registries, originating from the daemon, are forbidden. This behaviour can be overridden via a configuration change to /etc/docker/daemon.json.

The DIND image, created as part of initial post, included a version of the daemon.json. Assuming a registry hostname:port of registry:5000, add the following to the file.

{
    "hosts": [
        "unix:///var/run/docker.sock"
    ]
    ,"insecure-registries": ["registry:5000"]
}

For the changes to take effect, the image will need to be rebuilt.

$ cd $HOME/ubuntu-self-hosted-gh-runner
$ docker buildx build --load -t ubuntu-self-hosted-gh-runner:latest \
  --build-arg RUNNER_VERSION=$(curl -s -L -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "https://api.github.com/repos/actions/runner/releases/latest" | jq -r '.tag_name' | cut -c 2-) \
  --build-arg RUNNER_USER="runner" \
  --build-arg CONTEXT_ROOT_PATH="jammy_stable" \
  -f jammy_stable/Dockerfile .

Add Docker Registry Container to Docker Compose Stack

The official Distribution registry image available at Docker Hub will allow us to quickly add a local registry service to the docker compose stack.

Below is the revised version of the docker-compose.yml, which includes the service definition for the local registry.

version: "3.8"

services:
  self-hosted-runner:
    image: ubuntu-self-hosted-gh-runner:latest
    container_name: self-hosted-runner
    privileged: true
    volumes:
      - var-lib-docker:/var/lib/docker
    networks:
      - local-net
    environment:
      - REPO_OWNER=my-git-username
      - REPOSITORY=self-hosted-local-infra-test
      - RUNNER_ADMIN_TOKEN=<classic personal access token>
      - RUNNER_LABELS={"labels":["self-hosted","jammy-amd64"]}
      - RUNNER_NAME=self-hosted-jammy-amd64
      - RUNNER_CONFIG_ARGS=--unattended --replace --disableupdate --no-default-labels
      
  registry:
    image: registry:latest
    container_name: registry
    hostname: registry
    volumes:
      - ./auth:/auth
      - var-lib-registry:/var/lib/registry     
    ports:
      - "5000:5000"
    networks:
      - local-net
    environment:
      - REGISTRY_AUTH=htpasswd
      - REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm"
      - REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd
      - REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/var/lib/registry

networks:
  local-net:
    name: local-net

volumes:
  var-lib-docker:
    name: var-lib-docker
  var-lib-registry:
    name: var-lib-registry

The host:container volume mount for the registry service includes the following:

volumes:
  - ./auth:/auth

This allows us to generate credentials into host location ./auth for the registry container.

Configure Docker Registry Credentials

Credentials can be generated using the htpasswd utility.

To create a registry username/password of reguser/regpass :

$ docker run --entrypoint htpasswd httpd:2 -nbB reguser regpass > $PWD/auth/htpasswd

Testing

Bring up the compose stack:

$ docker compose up -d

Accessing the Registry from the Host Machine

To access the registry from our host machine, we use endpoint http://localhost:5000 with the credentials we created earlier.

$ docker login -u reguser -p regpass http://localhost:5000

A successful login returns:

WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/lts/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

Test Image Push from Host Machine to Registry

Pull an image from Docker Hub:

$ docker pull hello-world:latest

latest: Pulling from library/hello-world
...
...

Tag the image with local registry tag:

$ docker tag hello-world:latest localhost:5000/hello-world:latest

Push:

$ docker push localhost:5000/hello-world:latest

The push refers to repository [localhost:5000/hello-world]
ac28800ec8bb: Pushed
...
...

Test Access to Registry from Self-Hosted GitHub Runner

To ensure workflows executing on our self-hosted runner can access the local registry, we can use the following workflow.

name: Self-hosted Runner to Local Docker Registry test

on:
  push:

env:
  local-registry: "http://registry:5000"
## Hard coded for demo only, use repository secrets in all other cases
  registry-username: reguser
  registry-password: regpass

jobs:
  test-local-registry:
    runs-on: [self-hosted, jammy-amd64]
    steps:
      - name: Pull a test image from Docker Hub
        id: pull-test-img
        run: |
          docker pull docker.io/library/hello-world:latest

      - name: Prepare test image for upload to local registry
        id: tag-test-img
        run: |
          docker tag docker.io/library/hello-world:latest registry:5000/hello-world:latest

      - name: Login to local registry
        id: login
        uses: docker/login-action@v3
        with:
          registry: ${{ env.local-registry }}
          username: ${{ env.registry-username }}
          password: ${{ env.registry-password }}

      - name: Push the to local registry
        id: push-to-local
        run: |
          docker push registry:5000/hello-world:latest

The workflow should complete successfully with the following output.

Self Hosted To Local Registry Test Pull And Tag
Self Hosted To Local Registry Test Push To Local Registry
Series Navigation<< Local Self-Hosted Runner using Docker ComposeAdd Self-Hosted Apache Maven Registry >>