GitHub Actions runners
A Runner is a virtual machine in your Lane that runs GitHub Actions jobs for your repos. af creates the machine; you register it with GitHub using your own GitHub credential, so af never holds a GitHub token.
You decide:
- repo or organisation;
- its labels;
- one job or kept;
- what your workflow does when the runner is offline.
Create and register
Section titled “Create and register”af runner create ci1 --size M --timeout 900 --json # installed, not registered yet
# Create a registration token with your own GitHub credential and pipe it in.# af stores it nowhere, and it is never an argument.gh api -X POST repos/OWNER/REPO/actions/runners/registration-token --jq .token \ | af runner register ci1 --token - --url https://github.com/OWNER/REPO --labels my-label --jsonThen in your workflow:
jobs: build: runs-on: [self-hosted, my-label]- The first create takes a few minutes: about 6 to unpack the image, then about 2 to boot. Give it
--timeout 900. If it answersUNREACHABLEor says it did not answer, repeat the same create; it returns the same Runner with"created": false. A repeat may wait up to 11 minutes first. - Registration tokens last an hour. If yours fails,
af runner registerstops withINTERNAL, shows the runner’s own message (never the token) and leaves the Runner unchanged. Get a new token and repeat. - Registered already? A second
registerisNAME_TAKEN. To move a Runner to another repo, delete it, create it again and register it.
One job and gone
Section titled “One job and gone”An --ephemeral Runner takes one job, then GitHub removes its registration and the machine powers off. Register it with a JIT config:
af runner create once1 --ephemeral --jsongh api -X POST repos/OWNER/REPO/actions/runners/generate-jitconfig \ -f name=once1 -F runner_group_id=1 -f 'labels[]=self-hosted' -f 'labels[]=my-label' --jq .encoded_jit_config \ | af runner register once1 --jit-config - --jsonA JIT config has only the labels you name, so name self-hosted too if your workflow asks for it; otherwise the job stays queued. A finished ephemeral Runner still counts against your caps until you af runner delete it.
What is inside
Section titled “What is inside”Ubuntu 24.04, the Actions runner, Node 22, pnpm, git, Python 3, build tools, Java 17, Swift 6.4.0 (at /home/runner/toolchains/swift-6.4.0), the Android SDK (at /home/runner/android-sdk), Playwright’s system libraries, and TZ=Etc/UTC.
Jobs run as the user runner, without sudo. There is no SSH into a Runner: you reach it through GitHub and af runner logs.
--kvm asks for nested virtualisation, for jobs that start virtual machines of their own, such as Android test devices. Ask for it if your job needs it.
The cache
Section titled “The cache”A kept Runner has a 50 GB cache disk that holds its pnpm store, Turbo cache, ~/.gradle and Playwright browsers. It survives across jobs, and even when you delete the Runner and create the same name again. It counts against your Lane’s disk cap. af runner delete keeps it unless you pass --with-cache. Ephemeral Runners have none.
S, M and L work as for Boxes, on your Lane’s own cores. --size CI (16 GB) is made for Actions jobs: instead of holding cores of its own, it shares your Lane’s CPUs with your other machines. A Runner keeps the CPUs it was made with; if your admin changes your Lane’s CI setting, delete and re-create it.
Check on your Runners
Section titled “Check on your Runners”af runner list --json # live size, registered, busy, and how much cache it usesaf runner logs ci1 --json # the runner service's last lines (--lines up to 1000)A Runner that stopped, or an ephemeral one that finished: delete it and create the same name again. The cache stays.
Delete
Section titled “Delete”gh api -X POST repos/OWNER/REPO/actions/runners/remove-token --jq .token \ | af runner delete ci1 --confirm ci1 --removal-token - --jsonWith a removal token, af takes the Runner off GitHub first; if that fails, nothing is deleted. Deregistering cancels a job it is running. Without a token, GitHub drops an offline runner by itself. deregistered: false means it was never registered, or was registered with a JIT config, which GitHub removes on its own.
When the platform is down
Section titled “When the platform is down”A job whose label has no online runner waits up to 24 hours on GitHub, then fails (GitHub’s docs). If a workflow must survive your runner being offline, pick the runner in a first job, and fall back to a GitHub-hosted runner when yours is not online.