- Local Self-Hosted Runner using Docker Compose
- Adding a Local Docker Registry
- Add Self-Hosted Apache Maven Registry
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 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-registryThe host:container volume mount for the registry service includes the following:
volumes:
  - ./auth:/authThis 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/htpasswdTesting
Bring up the compose stack:
$ docker compose up -dAccessing 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:5000A 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 SucceededTest 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:latestPush:
$ 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:latestThe workflow should complete successfully with the following output.


