Local Self-Hosted Runner using Docker Compose

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

The main goal of this post is to create a local docker compose stack, comprising of single container/service, i.e., GitHub self-hosted runner. Some extra effort is involved in preparing the local infrastructure, however, once up and running, adding services to the stack becomes a trivial exercise, mostly involving modifications to the docker-compose.yml content.

Docker Compose Stack

Prerequisites and Content Overview

Examples throughout this article include references to GitHub resources with the following names:

  • Username: my-git-username
  • Git Repository:
    • URL: https://github.com/my-git-username/self-hosted-local-infra-test.git
    • Name: self-hosted-local-infra-test

Create the repository above if you intend on following along with the examples presented.

The process was tested on an Ubuntu 20.04 host, with the Docker Engine and Docker Compose installed.

In summary, the points covered are:

  • Generate a Classic Personal Access Token (PAT) to allow an entrypoint.sh script to programmatically register a self-hosted runner to the target repository
  • Clone an existing repository, containing a sample docker-compose.yml, and artifacts required to build the docker image
  • Build the GitHub Actions Runner agent docker image
  • Update the supplied docker-compose.yml to reflect the runner configuration
  • Run the stack and check the status of the runner
  • Test the infrastructure by running a test workflow

Generate GitHub PAT

Generate a classic personal access token for with repo scope.

Repo Pat Classic

Note down the generated token.

Download Project Files

Download/clone the repository containing sample project files:

cd $HOME
git clone https://github.com/techtoaster-io/ubuntu-self-hosted-gh-runner.git

Build Image

Build the image.

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

The command creates image ubuntu-self-hosted-gh-runner:latest.

Update docker-compose.yml

Values of the environment variables in file $HOME/ubuntu-self-hosted-gh-runner/docker-compose.yml will need to be updated to reflect your environment i.e.,:

   # GitHub username
  - REPO_OWNER=my-git-username

    # Name of the repository that the runner should be registered to 
  - REPOSITORY=self-hosted-local-infra-test

    # Personal Access token
  - RUNNER_ADMIN_TOKEN=<classic personal access token>

Variables specific to the self-hosted runner configuration are set the default values below, and can be changed as required.

   # JSON array of label names to assign to the runner
  - RUNNER_LABELS={"labels":["self-hosted","jammy-amd64"]}

    # Name to assign to the runner
  - RUNNER_NAME=self-hosted-jammy-amd64

    # Any additional arguments to pass to the ./config runner agent command
  - RUNNER_CONFIG_ARGS=--unattended --replace --disableupdate --no-default-labels

Run Docker Compose Project

Bring up the stack:

$ cd $HOME/ubuntu-self-hosted-gh-runner
$ docker compose up -d

Monitor the registration status of the self-hosted runner by following the container logs.

$ docker logs -f self-hosted-runner

Sample output:

---
...
NOTICE --- Labels received from env var RUNNER_LABELS (json formatted): self-hosted,jammy-amd64
DEBUG --- Configuring the runner.

--------------------------------------------------------------------------------
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___(_) |_| | | |_   _| |__      / \   ___| |_(_) ___  _ __  ___      |
|      | |  _| | __| |_| | | | | '_ \    / _ \ / __| __| |/ _ \| '_ \/ __|     |
|      | |_| | | |_|  _  | |_| | |_) |  / ___ \ (__| |_| | (_) | | | \__ \     |
|       \____|_|\__|_| |_|\__,_|_.__/  /_/   \_\___|\__|_|\___/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
--------------------------------------------------------------------------------

# Authentication
√ Connected to GitHub
# Runner Registration

√ Runner successfully added
√ Runner connection is good

# Runner settings

√ Settings Saved.
...
DEBUG --- /runner/config.sh --url https://github.com/***********/self-hosted-local-infra-test --token ********** 
           --labels self-hosted,jammy-amd64 --unattended --replace --disableupdate 
           --no-default-labels --name self-hosted-jammy-amd64
DEBUG --- Runner successfully configured.
...
√ Connected to GitHub

Current runner version: '2.********** '
Listening for Jobs
  • The log confirms the runner was registered successfully with the following labels:
    • self-hosted
    • jammy-amd64
  • Check repository Actions settings to ensure the runner appears
Repo Runner Status

Run Test Workflow

Create a sample workflow similar to the following (slightly modified version of github-actions-demo.yml):

name: GitHub Actions Demo for self-hosted runner
run-name: Test out GitHub Actions🚀
on: [push]
jobs:
  GitHub-Actions-Test:
    runs-on: [self-hosted, jammy-amd64]
    steps:
      - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
      - run: |
          echo "🐧 This job is now running on a self-hosted runner with named: ${{ runner.name }}"
      - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
      - name: Check out repository code
        uses: actions/checkout@v4
      - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
      - run: echo "🖥️ The workflow is now ready to test your code on the runner."
      - name: List files in the repository
        run: |
          ls ${{ github.workspace }}
      - run: echo "🍏 This job's status is ${{ job.status }}."

Push the changes.

git add -A 
git commit -m "add git-actions-demo.yml"
git push -u origin master

The workflow should execute on our self-host runner.

Check the repository’ workflow logs to ensure all steps executed successfully.

Github Actions Test Workflow
Series NavigationAdding a Local Docker Registry >>