Special hardware, private networks or very high volume make GitHub-hosted runners too limited or too expensive.
GitHub-hosted runners are fresh VMs per job. Alternatives:
- Larger runners: GitHub-hosted machines with more CPU and RAM, GPUs or static IPs, configured per organization and selected by label.
- Self-hosted runners: the open-source runner application installed on your own machine or VM. It opens an outbound HTTPS long-poll to GitHub to receive jobs, so no inbound firewall rules are needed.
Jobs select runners with runs-on, which can be a label list such as [self-hosted, linux, x64, gpu] (all must match) or group: plus labels: for runner groups. Runner groups control which repositories may use which runners.
The big caveat: a self-hosted runner is not reset between jobs unless you make it so. Files, processes, Docker images and credentials can survive from one job to the next. For that reason GitHub warns against self-hosted runners on public repositories, where any fork PR could run code on your machine. Prefer ephemeral runners (--ephemeral) that take a single job and are destroyed.
At scale, Actions Runner Controller (ARC) runs ephemeral runners as Kubernetes pods using runner scale sets, scaling from zero with demand. Keep runners patched, give them minimal cloud permissions and isolate them per trust level.
Example
# Register an ephemeral runner (token from Settings > Actions > Runners)
./config.sh --url https://github.com/my-org --token "$REG_TOKEN" \
--labels gpu,linux-x64 --runnergroup ml --ephemeral --unattended
./run.sh # takes exactly one job, then exits
# Workflow side (YAML):
# runs-on: [self-hosted, gpu]
# or
# runs-on:
# group: ml
# labels: [gpu]
# Kubernetes: Actions Runner Controller scale set via Helm
helm install arc-runners \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
--namespace arc-runners --set githubConfigUrl=https://github.com/my-orgRemember
- Self-hosted runners poll GitHub over outbound HTTPS
- runs-on labels must all match; runner groups limit repository access
- Use ephemeral runners or ARC so no state leaks between jobs
Watch out for
- Attaching a persistent self-hosted runner to a public repository, letting fork PRs run code on it.