Topic 2.4
Self-Hosted Runners
In one line
A hosted runner is fresh, disposable infrastructure GitHub manages for you — a self-hosted runner is your own machine instead, needed when a job requires specific hardware, network access, or software hosted runners can't provide.
Key ideas
- 01
Every example so far has used
runs-on: ubuntu-latest— a HOSTED runner, a fresh virtual machine GitHub provisions, uses for exactly one job, and destroys immediately afterward. This is genuinely the right default: zero maintenance, always clean, and billed only for actual usage time. - 02
A SELF-HOSTED RUNNER is your OWN machine (physical, virtual, or a container) registered with GitHub specifically to run jobs from your repository or organization —
runs-on: [self-hosted, linux, gpu]targets it using custom LABELS you define, instead of a hosted runner's fixed image names. - 03
The genuine real reasons to reach for a self-hosted runner: needing SPECIFIC HARDWARE hosted runners don't offer (a GPU for a machine-learning build step), needing NETWORK ACCESS to a private, internal resource hosted runners genuinely can't reach (an internal artifact repository behind a VPN, echoing the DevOps roadmap's own artifact-management section), or needing SOFTWARE/LICENSES that are impractical to install fresh on every single hosted-runner run.
- 04
A self-hosted runner, UNLIKE a hosted one, is NOT automatically fresh and disposable between jobs by default — it persists as a real, standing machine, meaning artifacts, cached files, or even malicious code from a PREVIOUS job could genuinely still be present when the NEXT job runs, unless you deliberately configure cleanup between runs.
- 05
This is a genuinely real SECURITY concern specifically for self-hosted runners on PUBLIC repositories — anyone who can open a pull request could potentially get their own (possibly malicious) workflow code executed on your own actual infrastructure; GitHub's own documentation explicitly warns against using self-hosted runners on public repos without carefully restricting which workflows can actually trigger them.
- 06
Registering a self-hosted runner is genuinely simple mechanically (download GitHub's runner agent, run a registration script with a token from your repository's settings, start the agent as a background service) — the real complexity is entirely operational: keeping the runner's own OS and software patched, monitoring its health, and managing its capacity as demand grows, exactly the kind of ongoing infrastructure responsibility a hosted runner exists specifically to remove from your plate.
Code & diagrams
Fresh and disposable versus persistent and yours to maintain — a genuine, deliberate trade-off, not a strict upgrade.
Targeting a self-hosted runner by custom labels, for a job needing a GPU.
jobs:
train-model:
runs-on: [self-hosted, linux, gpu] # custom labels, not a hosted image name
steps:
- uses: actions/checkout@v4
- name: Run GPU-accelerated training
run: python train.py --device cuda
# Explicit cleanup — genuinely necessary on a persistent, self-hosted machine
- name: Clean workspace
if: always()
run: rm -rf ${{ github.workspace }}/*Explain it without notes
What's the actual difference in lifecycle between a hosted runner and a self-hosted runner, and why does that difference create a real security consideration?
Give one genuine, concrete real reason a team would need a self-hosted runner rather than simply using a hosted one.
Practice
If you have access to a spare machine (even a local VM), register it as a self-hosted runner for a test repository and confirm a job targeting its specific labels actually runs on it.
Think through, for a hypothetical job needing access to an internal, VPN-only resource, why a hosted runner genuinely couldn't do this job at all, regardless of how it's configured.
Trade-offs
- ↔
Self-hosted runners solve real problems hosted runners genuinely cannot, but they trade GitHub's own zero-maintenance guarantee for real, ongoing operational responsibility (patching, monitoring, capacity planning, and genuine security hardening) that YOU now own — the right call specifically when a concrete, real need (specific hardware, private network access) actually exists, and a real cost to take on otherwise.
Done when you can
I understand the lifecycle difference between a hosted and a self-hosted runner.
I can name genuine real reasons to need a self-hosted runner rather than a hosted one.
I understand the real security consideration self-hosted runners introduce, especially on public repositories.