-
-
Notifications
You must be signed in to change notification settings - Fork 408
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
Note that if an org runner needs to be available there may be some settings to toggle such as Allow Public Repositories below.
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