Git, CI/CD & GitOpsXL · RunnersRunners
Hosted runners — convenience, isolation, and the control you give up
What you'll learn
- Explain what a GitHub-hosted runner is and who owns the host
- Identify the runtime that the runner image provides
- Recognise the isolation model: a fresh VM per job, no state between jobs
- Articulate what you do not control on a hosted runner: image contents, installed tools, network egress, kernel
- Decide when hosted runners fit and when they do not
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A GitHub-hosted runner is a virtual machine that GitHub owns,
patches, and destroys. The workflow asks for runs-on: ubuntu-latest and GitHub hands the job a freshly-provisioned
VM with a curated toolchain, runs the steps, and tears the VM
down. The engineer who wrote the workflow never logs into the
host, never patches the kernel, and never cleans up the disk.
That convenience has a shape. Knowing the shape is what separates a workflow that runs reliably on hosted runners from one that hits an obscure limitation at 3am.
What GitHub provides
flowchart LR
A["Workflow job"] -->|"runs-on: ubuntu-latest"| B["GitHub-hosted VM"]
B -->|"fresh VM,\ndestroyed at job end"| C["Step execution"]
C -->|"logs, artifacts"| D["Workflow result"]
A hosted runner comes with:
- A standardised image. GitHub publishes the contents of
each image (
ubuntu-latest,windows-latest,macos-latest) as a manifest. The image is rebuilt and patched continuously; the engineer references the label, not a version. - A curated toolchain. Common languages and tools — Python, Node, Go, Java, Docker, Git, build-essential, OpenSSL — are preinstalled. Versions track the upstream.
- A fixed hardware class. Two vCPUs, 7 GB RAM, 14 GB SSD
on the standard runners; larger classes (
large,xlarge) are available on paid plans. - An ephemeral disk. The VM is destroyed when the job
ends. Files written to
/tmpor$RUNNER_TEMPsurvive only inside the job.
The isolation model
flowchart TB
subgraph JOB1["Job 1"]
V1["Fresh VM A"]
end
subgraph JOB2["Job 2 (same workflow, later step)"]
V2["Fresh VM B"]
end
subgraph JOB3["Job 3 (next workflow run)"]
V3["Fresh VM C"]
end
Every job, on every workflow, gets a new VM. State from a
previous job is unreachable: there is no shared filesystem,
no leftover process, no ~/.npm cache from a previous run.
Two concurrent jobs for the same workflow run on two different
hosts. The blast radius of one buggy job is one VM that no
longer exists.
What you do not control
Hosted runners have limits that bite teams that try to push them past their design point:
- You do not control the image. You cannot
apt install <tool>and expect it next month. You can install tools per job (this is whatactions/setup-*actions do), but the base image is GitHub’s. - You do not control network egress. Hosted runners can reach the public internet by default. They cannot reach your private VPC unless you expose it to the internet (a configuration to be avoided) or use a runner inside the VPC (which means self-hosted).
- You do not control the kernel or hardware. GPU acceleration is not available; nested virtualisation is not available; some kernel features needed for eBPF, for example, may not be present.
- You do not control concurrency. On free plans, jobs queue when the per-repo concurrent limit is hit. On paid plans, limits are higher but still capped.
When hosted runners fit
Hosted runners are the right choice when:
- The workflow does not need private-network access.
- The toolchain fits inside the curated image (or is installed
per job with
actions/setup-*). - The job completes within the per-job timeout.
- The data the job touches is not regulated to the point of forbidding third-party VMs.
For a public open-source project, a hosted runner is almost always the answer. For a regulated enterprise pipeline that must run inside a private VPC, a hosted runner is almost always not the answer.
Production discipline
- Treat
runs-on: ubuntu-latestas a moving target. Pin by full label (ubuntu-22.04) when reproducibility matters. - Install per-job, not via base image. Workflows that
apt installto extend the base image break when the base image changes. - Use caching actions. Reinstalling every job is slow;
actions/cacherecovers some of the speed. - Assume the runner has open egress. Treat secrets accordingly; use OIDC for short-lived cloud credentials rather than long-lived secrets.
Cross-course references
- Ansible for Production Sysadmins - Part XXXVII (RepoArch) covers CI runner selection patterns.
- Linux for Production Sysadmins - Part XXXIII (NetHard) covers the egress-controls story that motivates self-hosted runners in Part XL-02.
- Terraform for Production Sysadmins - Part XLII (TerraformCloud) discusses how Terraform Cloud fits into the hosted/self-hosted runner decision.
Quiz
Knowledge check · 4 questions
Q1. A workflow uses `runs-on: ubuntu-latest` and runs `apt install my-package` in a setup step. Six weeks later the same workflow stops working during setup. What is the most likely cause?
Q2. GitHub-hosted runners provide a fresh VM per job, which prevents state from one workflow from leaking into another.
Q3. Name two things you do not control on a GitHub-hosted runner that you would control on a self-hosted runner.
Q4. Choose between hosted and self-hosted runners for a given workflow and justify the choice.
Team T runs two workflows. Workflow A builds an open-source CLI and publishes a release artifact to GitHub. Workflow B deploys infrastructure into a private VPC using Terraform and an internal artifact registry that has no public DNS. Team T currently uses hosted runners for both.
Passing score: 75%. Answers are checked in this browser.