Github Actions

Github Actions

This guide will walk you through setting up self-hosted GitHub Actions runners with Docker on your machine.

Create self hosted runner

A GitHub Actions self-hosted runner is a machine (physical or virtual) that runs workflows from GitHub Actions. Instead of using GitHub’s default cloud runners, you can self-host your own runner inside a Docker container to have more control over the environment.

Base runner image

Create the following Dockerfile:

Dockerfile.ga-base
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# base
FROM ubuntu:latest

# set environment variables for non-interactive installation
ENV DEBIAN_FRONTEND=noninteractive

# update the base packages
RUN apt update -y && apt upgrade -y && apt dist-upgrade -y

# install build essential
RUN apt install build-essential -y

# install curl
RUN apt install curl -y

# install wget
RUN apt install wget -y

# install unzip
RUN apt install unzip -y

# install git
RUN apt install git -y

# install jq
RUN apt install jq -y

# install hugo - https://github.com/gohugoio/hugo/releases
RUN wget -q https://github.com/gohugoio/hugo/releases/download/v0.142.0/hugo_0.142.0_linux-amd64.deb -O hugo.deb
RUN dpkg -i hugo.deb
RUN rm hugo.deb

# install nodejs - https://github.com/nodesource/distributions
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x -o nodesource_setup.sh
RUN bash nodesource_setup.sh
RUN rm nodesource_setup.sh
RUN apt install nodejs -y

# install awscli - https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
RUN curl -s "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN rm awscliv2.zip
RUN ./aws/install
RUN rm -rf aws

Run the following command to create base runner image:

docker build -f Dockerfile.ga-base -t ga-base-image:latest .
How to fix docker build error on macOS

Forcing docker to use linux/amd64 platform by default on macOS, during the build add this extra option:

--platform linux/amd64

Runner image

Create the following Dockerfile:

Dockerfile.ga-runner
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# base
FROM ga-base-image:latest

# add new user
RUN useradd -m docker
WORKDIR /home/docker

# install some additional dependencies
RUN apt install libicu-dev -y

# download and unzip the github actions runner
ARG RUNNER_VERSION
RUN mkdir actions-runner
WORKDIR /home/docker/actions-runner
RUN curl -s -o actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
RUN tar xzf ./actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
RUN rm actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz

# install dependencies from actions-runner
RUN ./bin/installdependencies.sh

# add start.sh script
ADD start.sh start.sh

# make the script executable
RUN chmod +x start.sh

# set the user to "docker" so all subsequent commands are run as the docker user
USER docker

# set the entrypoint to the start.sh script
ENTRYPOINT ["./start.sh"]

Create the following entry point file:

start.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

GH_ORG=$GH_ORG
GH_TOKEN=$GH_TOKEN
GH_RUNNER_PREFIX=$GH_RUNNER_PREFIX
GH_RUNNER_SUFFIX=$(cat /dev/urandom | tr -dc 'a-z' | fold -w 5 | head -n 1)
GH_RUNNER_NAME="${GH_RUNNER_PREFIX}-docker-${GH_RUNNER_SUFFIX}"
GH_REG_TOKEN=$(curl -sX POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${GH_TOKEN}" https://api.github.com/orgs/${GH_ORG}/actions/runners/registration-token | jq .token --raw-output)

./config.sh --unattended --url https://github.com/${GH_ORG} --token ${GH_REG_TOKEN} --name ${GH_RUNNER_NAME}

cleanup() {
  echo "Removing runner..."
  ./config.sh remove --token ${GH_REG_TOKEN}
  echo "Removed"
  sleep 1
  exit 0
}

trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM

./run.sh & wait $!

Get the latest runner version from here, and then use the following command to create the runner image:

docker build --build-arg RUNNER_VERSION=2.321.0 -f Dockerfile.ga-runner -t ga-runner-image:latest .
Run a Docker image as a container

Run the following command to start a container from the Ubuntu image and launch an interactive Bash shell:

docker run -i -t ubuntu:latest /bin/bash

Run the runners

Create the following Docker-compose:

docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
services:
  runner:
    image: ga-runner-image:latest
    restart: unless-stopped
    mem_limit: 1024m
    cpus: '1.0'
    networks:
      - runner-network
    environment:
      GH_TOKEN: "${GH_TOKEN}"
      GH_ORG: "${GH_ORG}"
      GH_RUNNER_PREFIX: "${GH_RUNNER_PREFIX}"

networks:
  runner-network:
    name: runner-network
    driver: bridge

Create .env file as following:

.env
1
2
3
GH_TOKEN=github_pat_******
GH_ORG=zeroerrorcode
GH_RUNNER_PREFIX=

Get the GH_TOKEN from here, and then run the following commnad to s:

docker compose -f docker-compose.yml up --scale runner=1 --no-recreate -d

Run github workflows in local

Using the GitHub Act package, we can run actions locally, enabling faster feedback without the need to commit and push changes to GitHub for testing.

Getting started

Install Act on your machine using the following command:

brew install act # install act
act -l # list workflows

Create actrc file in repo:

.actrc
1
2
3
--pull=false
-P self-hosted=ga-base-image:latest
--container-architecture linux/amd64

Create secrets file in repo and make sure it is added in .gitignore:

.secrets
1
2
3
4
5
KEY1=VALUE1
KEY2=VALUE2
KEY3=VALUE3
...
KEYN=VALUEN
.gitignore
10
11
12
13
14
15
...

# Misc
.secrets

...
Last updated on