- 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-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.

