-
-
Notifications
You must be signed in to change notification settings - Fork 397
Usage
Creating GitHub personal access token (PAT) for using by self-hosted runner make sure the following scopes are selected:
- repo (all)
- admin:org (all) (mandatory for organization-wide runner)
- admin:enterprise (all) (mandatory for enterprise-wide runner)
- admin:public_key - read:public_key
- admin:repo_hook - read:repo_hook
- admin:org_hook
- notifications
- workflow
To use the runner you will need to enable the organization_self_hosted_runners permission with read and write
Here's an example service definition for systemd:
# Install with:
# sudo install -m 644 ephemeral-github-actions-runner.service /etc/systemd/system/
# sudo systemctl daemon-reload
# sudo systemctl enable ephemeral-github-actions-runner
# Run with:
# sudo systemctl start ephemeral-github-actions-runner
# Stop with:
# sudo systemctl stop ephemeral-github-actions-runner
# See live logs with:
# journalctl -f -u ephemeral-github-actions-runner.service --no-hostname --no-tail
[Unit]
Description=Ephemeral GitHub Actions Runner Container
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/docker stop %N
ExecStartPre=-/usr/bin/docker rm %N
ExecStartPre=-/usr/bin/docker pull myoung34/github-runner:latest
ExecStart=/usr/bin/docker run --rm \
--env-file /etc/ephemeral-github-actions-runner.env \
-e RUNNER_NAME=%H \
-v /var/run/docker.sock:/var/run/docker.sock \
--name %N \
myoung34/github-runner:latest
[Install]
WantedBy=multi-user.target
And an example of the corresponding env file that the service reads from:
# Install with:
# sudo install -m 600 ephemeral-github-actions-runner.env /etc/
RUNNER_SCOPE=repo
REPO_URL=https://github.com/your-org/your-repo
# Alternate for org scope:
#RUNNER_SCOPE=org
#ORG_NAME=your-org
LABELS=any-custom-labels-go-here
ACCESS_TOKEN=foo-access-token
RUNNER_WORKDIR=/tmp/runner/work
DISABLE_AUTO_UPDATE=1
EPHEMERAL=1
GitHub's hosted runners are completely ephemeral. You can remove all its data without breaking all future jobs.
To achieve the same resilience in a self-hosted runner:
- set
EPHEMERAL=1
in the container's environment - don't mount a local folder into
RUNNER_WORKDIR
(to ensure no filesystem persistence) - run the container with
--rm
(to delete it after termination) - wrap the container execution in a system service that restarts (to start a fresh container after each job)
This project runs the container as root
by default.
Running as non-root is non-default behavior that is supported via an environment variable RUN_AS_ROOT
. Default value is true
.
- If
true
: preserve old behavior and run as root - If
true
and user is provided with-u
(or any orchestrator equiv): error and exit - if
false
: run container as root and assumerunner
user via gosu - if
false
and user is provided with-u
(or any orchestrator equiv): run entire container asrunner
user
The runner user is runner
with uid 1001
and gid 121
If you'd like to run the whole container as non-root:
- Set the environment variable
RUN_AS_ROOT
tofalse
- Ensure
RUNNER_WORKDIR
is either not provided (/_work
by default) or permissions are correct. therunner
user cannot change a directories permissions in entrypoint.sh that it does not have access to - add
-u runner
or-u 1001
to the docker command. In k8s this would besecurityContext.runAsUser
. Nomad, etc would all do this differently.
The runner image can be customized by modifying the config.json file.
If a specific user and group ID are required to align with a user on the host,
they can be set by modifying the user-id
and group-id
fields:
{
"user": {
"user-id": 1001,
"group-id": 121
}
}
Packages can be added or removed from the image by changing the install
field.
An example is the following:
{
"install": [
{
"category": "sdks",
"source": "apt",
"packages": [
"nodejs",
"python3",
"openjdk-21-jdk"
]
},
{
"category": "clis",
"source": "script",
"packages": [
"aws-cli",
"github-cli"
]
}
]
}
The category
name is arbitrary and is used only to document related packages together.
Any number of packages can be specified in each category.
Sources:
- For
apt
-sourced packages, any package can be installed for the sources configured in sources.sh. - For
script
-sourced packages, a function must exist in tools.sh with the nameinstall_<package>
.
name: Package
on:
release:
types: [created]
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v1
- name: build packages
run: make all
version: '2.3'
services:
worker:
image: myoung34/github-runner:latest
environment:
REPO_URL: https://github.com/example/repo
RUNNER_NAME: example-name
RUNNER_TOKEN: someGithubTokenHere
RUNNER_WORKDIR: /tmp/runner/work
CONFIGURED_ACTIONS_RUNNER_FILES_DIR: /runner/data # Required for persistence
RUNNER_GROUP: my-group # github enterprise only
RUNNER_SCOPE: 'repo'
LABELS: linux,x64,gpu
security_opt:
# needed on SELinux systems to allow docker container to manage other docker containers
- label:disable
volumes:
- '/var/run/docker.sock:/var/run/docker.sock'
- '/runner/data:/runner/data' # required for persistence
- '/tmp/runner:/tmp/runner'
# note: a quirk of docker-in-docker is that this path
# needs to be the same path on host and inside the container,
# docker mgmt cmds run outside of docker but expect the paths from within
job "github_runner" {
datacenters = ["home"]
type = "system"
task "runner" {
driver = "docker"
env {
ACCESS_TOKEN = "footoken"
RUNNER_NAME_PREFIX = "myrunner"
RUNNER_WORKDIR = "/tmp/github-runner-your-repo"
RUNNER_GROUP = "my-group"
RUNNER_SCOPE = "org"
ORG_NAME = "octokode"
LABELS = "my-label,other-label"
}
config {
image = "myoung34/github-runner:latest"
privileged = true
userns_mode = "host"
volumes = [
"/var/run/docker.sock:/var/run/docker.sock",
"/tmp/github-runner-your-repo:/tmp/github-runner-your-repo",
]
}
}
}
apiVersion: apps/v1
kind: Deployment
metadata:
name: actions-runner
namespace: runners
spec:
replicas: 1
selector:
matchLabels:
app: actions-runner
template:
metadata:
labels:
app: actions-runner
spec:
volumes:
- name: containerdsock
hostPath:
path: /run/containerd/containerd.sock
- name: workdir
hostPath:
path: /tmp/github-runner-your-repo
containers:
- name: runner
image: myoung34/github-runner:latest
env:
- name: START_DOCKER_SERVICE
value: "true"
- name: ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: actions-runner
key: ACCESS_TOKEN
- name: RUNNER_SCOPE
value: "org"
- name: ORG_NAME
value: octokode
- name: LABELS
value: my-label,other-label
- name: RUNNER_NAME_PREFIX
value: foo
- name: RUNNER_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: RUNNER_WORKDIR
value: /tmp/github-runner-your-repo
- name: RUNNER_GROUP
value: my-group
volumeMounts:
- name: containerdsock
mountPath: /run/containerd/containerd.sock
- name: workdir
mountPath: /tmp/github-runner-your-repo
securityContext:
privileged: true # Required if you're enabling docker
resources:
limits:
cpu: 2
memory: 512Mi
requests:
cpu: 2
memory: 256Mi
A runner token can be automatically acquired at runtime if ACCESS_TOKEN
(a GitHub personal access token) is a supplied. This uses the GitHub Actions API. e.g.:
docker run -d --restart always --name github-runner \
-e ACCESS_TOKEN="footoken" \
-e RUNNER_NAME="foo-runner" \
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
-e RUNNER_GROUP="my-group" \
-e RUNNER_SCOPE="org" \
-e ORG_NAME="octokode" \
-e LABELS="my-label,other-label" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
myoung34/github-runner:latest
docker run -d --restart always --name github-runner \
-e ACCESS_TOKEN="footoken" \
-e RUNNER_NAME="foo-runner" \
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
-e RUNNER_GROUP="my-group" \
-e RUNNER_SCOPE="enterprise" \
-e ENTERPRISE_NAME="my-enterprise" \
-e LABELS="my-label,other-label" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
myoung34/github-runner:latest
docker run -d --restart always --name github-runner \
-e RUNNER_NAME_PREFIX="myrunner" \
-e ACCESS_TOKEN="footoken" \
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
-e RUNNER_GROUP="my-group" \
-e RUNNER_SCOPE="org" \
-e DISABLE_AUTO_UPDATE="true" \
-e ORG_NAME="octokode" \
-e LABELS="my-label,other-label" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
myoung34/github-runner:latest
docker run -d --restart always --name github-runner \
-e REPO_URL="https://github.com/myoung34/repo" \
-e RUNNER_NAME="foo-runner" \
-e RUNNER_TOKEN="footoken" \
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
-e RUNNER_GROUP="my-group" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
myoung34/github-runner:latest
function github-runner {
name=github-runner-${1//\//-}
org=$(dirname $1)
repo=$(basename $1)
tag=${3:-latest}
docker rm -f $name
docker run -d --restart=always \
-e REPO_URL="https://github.com/${org}/${repo}" \
-e RUNNER_TOKEN="$2" \
-e RUNNER_NAME="linux-${repo}" \
-e RUNNER_WORKDIR="/tmp/github-runner-${repo}" \
-e RUNNER_GROUP="my-group" \
-e LABELS="my-label,other-label" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-${repo}:/tmp/github-runner-${repo} \
--name $name myoung34/github-runner:latest
}
github-runner your-account/your-repo AARGHTHISISYOURGHACTIONSTOKEN
github-runner your-account/some-other-repo ARGHANOTHERGITHUBACTIONSTOKEN ubuntu-focal
This can be propogated to all other approaches
# per repo
docker run -d --restart always --name github-runner \
-e REPO_URL="https://github.com/myoung34/repo" \
-e RUNNER_NAME="foo-runner" \
-e RUNNER_TOKEN="footoken" \
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
-e RUNNER_GROUP="my-group" \
-e CONFIGURED_ACTIONS_RUNNER_FILES_DIR="/actions-runner-files" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
-v /tmp/foo:/actions-runner-files \
myoung34/github-runner:latest
To run the github runners behind a proxy, you need to pass the proxy parameters required for the GitHub Runner as environment variables.
Note: The http://
as prefix is required by the GitHub Runner.
docker run -d --restart always --name github-runner \
-e https_proxy="http://myproxy:3128" \
-e http_proxy="http://myproxy:3128" \
-e RUNNER_NAME_PREFIX="myrunner" \
# ...
myoung34/github-runner:latest