Self-hosted vs GitHub-hosted runners: which should you use?

A GitHub-hosted runner is a fresh virtual machine that GitHub provisions for each GitHub Actions job and bills per minute. A self-hosted runner is a machine you register with GitHub and operate yourself, and GitHub does not charge for the minutes it runs as of November 2026. GitHub-hosted runners suit public repositories, small teams and macOS or Windows builds; self-hosted runners pay off when you need bigger machines, GPUs, private network access, jobs longer than six hours or lower cost at high volume, and you can secure and patch the fleet.

How the two options compare

The figures below come from GitHub’s documentation for GitHub-hosted runners, larger runners and Actions limits, checked in November 2026.

GitHub-hosted (standard) Self-hosted
Who runs the machine GitHub, one new VM per job You, on any hardware or cloud
Standard Linux size 2 vCPU, 8 GB RAM, 14 GB SSD (private repos); 4 vCPU, 16 GB RAM (public repos) Whatever you provision
Maximum job time 6 hours 5 days
Queue limit Not applicable Jobs are cancelled after 24 hours in the queue
Concurrent jobs 20 (Free), 40 (Pro), 60 (Team), 500 (Enterprise) Limited by your capacity
GPUs and static IPs Only on larger runners Your choice
Access to private networks Not by default Runs inside your network
GitHub charge Per minute in private repos; free in public repos None as of November 2026
Maintenance None OS, runner images, autoscaling, security

Larger runners sit between the two. They are GitHub-hosted machines with more cores, GPUs or static IP addresses, available on the Team and Enterprise Cloud plans and billed per minute even for public repositories. Static IP addresses for larger runners are an Enterprise Cloud option. The only GPU larger runner has 4 vCPUs, 28 GB of RAM and one NVIDIA Tesla T4 with 16 GB of video memory.

When GitHub-hosted runners fit better

GitHub-hosted runners remove all operational work. Every job starts on a clean VM, so a compromised build cannot persist into the next one, and the images come with common toolchains preinstalled. They are the default choice when:

  • The repository is public. Standard GitHub-hosted runners are free there, and self-hosting on a public repository is risky (see below).
  • Build volume is low or bursty, so you would pay for idle self-hosted capacity most of the time.
  • You need macOS or Windows builds, which are expensive to host yourself.
  • Nobody on the team owns CI infrastructure.

The cost of this convenience grows with volume. The GitHub Actions pricing breakdown shows the per-minute rates and the monthly amounts each plan includes.

When self-hosting pays off

Self-hosted runners make sense when a hosted VM cannot do the job, or when the per-minute bill outgrows the cost of running your own machines:

  • Bigger or special hardware. Builds that need 32 cores, large memory, local NVMe caches, arm64 servers or GPUs.
  • Private network access. Integration tests against internal databases, artifact repositories or staging clusters that are not reachable from the internet.
  • Long jobs. Anything that runs past the 6-hour limit for GitHub-hosted jobs.
  • Location control. Builds that must run in a specific country or data center.
  • Warm caches. A runner that keeps dependency and container layer caches on local disk can cut minutes from every build.

Self-hosting does not make CI free. You pay for the machines, for idle capacity between jobs and for the time spent operating them.

The security trade-off

The main risk of self-hosting is untrusted code. GitHub’s secure use reference states that GitHub-hosted runners run each job in a clean, isolated VM, while self-hosted runners “can be persistently compromised by untrusted code in a workflow.” GitHub recommends against self-hosted runners on public repositories, because anyone can open a pull request, and notes the same risk applies to private repositories when anyone who can fork the repository can open a pull request.

Practical controls:

  • Use just-in-time (JIT) or ephemeral runners that run one job and are then removed, so a compromised job cannot affect the next one.
  • Separate runners into runner groups by trust level, and restrict which repositories and workflows can use each group.
  • Limit network access. Block cloud metadata endpoints and internal services that build jobs do not need.
  • Isolate the runner host. Do not share nodes between CI jobs and production workloads. For pull requests from outside contributors, use a sandboxed runtime; the article on isolating untrusted workloads compares gVisor, Kata Containers and Firecracker.

What you take on when you self-host

GitHub’s self-hosted runner documentation is explicit: the runner application updates itself, but “you are responsible for updating the operating system and all other software.” In practice, a self-hosted setup needs:

  • Runner images with the toolchains your workflows expect, rebuilt and patched on a schedule.
  • Autoscaling, so capacity follows the job queue and does not sit idle overnight.
  • Caching for dependencies and container layers, which hosted runners get from the GitHub cache service.
  • Monitoring of queue time and failed registrations.

On Kubernetes, the Actions Runner Controller handles registration and autoscaling by running each job in an ephemeral Pod.

A third option: managed self-hosted runners

Several companies sell runners that register with GitHub as self-hosted runners but run on the vendor’s infrastructure. You change the runs-on label and pay the vendor instead of GitHub. The vendor operates the machines and images, so you get larger hardware without running it yourself. Check where the machines run, how jobs are isolated from other customers and how the vendor’s per-minute price compares with GitHub’s larger runners.

Running runners on Cloudfleet

Self-hosted runners can run as Pods on a Cloudfleet Kubernetes Engine (CFKE) cluster. Fleet taints dedicate a Fleet to CI runners next to the Fleet that runs production services. Runners that build pull requests from external contributors can run in Kata Containers with VM-level isolation.

On this page