GitHub-Hosted vs Self-Hosted Runners

GitHub operates the machine for GitHub-hosted runners and you operate it for self-hosted runners. Managed runners are a third shape. Full comparison here.

Last verified:

Answer

GitHub-hosted runners are machines GitHub provisions, patches, and bills by the minute, and self-hosted runners are machines you provision, patch, and pay for yourself before registering them against a repository, organization, or enterprise. Managed runners are a third shape: a vendor operates the fleet and registers it with GitHub through the same self-hosted registration path, so GitHub records a self-hosted runner while the vendor carries the operational work.

The runner software is the same in all three cases. What changes is ownership of the machine underneath it, and every practical difference follows from that one fact: who builds the image, who holds the capacity when no job is running, what the machine is allowed to reach on the network, and which bill the minutes land on.

The table below compares the three shapes on the dimensions that decide the choice. The managed column describes WarpBuild, which registers itself as a self-hosted runner in the Default runner group (id 1) of your GitHub organization, per the WarpBuild public repositories documentation.

DimensionGitHub-hostedSelf-hosted you operateManaged runners (WarpBuild)
Who patches imagesGitHub, on its own release cadenceYou, including the toolchain versions each job expectsWarpBuild, on published images
Who pays for idle capacityNobody. Billing starts and stops with the jobYou. Instances bill for wall-clock time whether or not a job is runningNobody. Billing is per minute of job time
Isolation modelFresh virtual machine per job, destroyed afterwardsWhatever you build. Container-based fleets such as ARC share a host kernelFresh virtual machine per job, destroyed afterwards, per the security documentation
Network placementGitHub's network. Private services need a tunnel or an allow listYour network, so private endpoints resolve directlyWarpBuild's cloud, or your own cloud account and network on BYOC
Billing shapePer minute on your GitHub bill, with included minutes on some plansCloud instance hours plus storage, plus the engineering time to run the fleetPer minute, usage based, on the WarpBuild bill
Public repository accessAvailable by defaultBlocked until a runner group allows public repositoriesSame runner group setting applies
Dependabot updatesRun by defaultRequire the self-hosted Dependabot setting and a labeled runnerSame requirement as self-hosted

Detail

How GitHub routes a job to each kind

Routing is a label match. runs-on: ubuntu-latest sends the job to GitHub's fleet. An array such as runs-on: [self-hosted, linux, x64] sends it to a registered runner carrying all three labels. A managed runner is requested by the label the vendor publishes, which for WarpBuild is a warp- prefixed label naming the image and the size.

One workflow can mix all three, which is the fastest way to see the difference on a repository you already have.

name: build
on:
  push:
    branches: [main]
  pull_request:

jobs:
  on_github_hosted:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: make test

  on_self_hosted:
    runs-on: [self-hosted, linux, x64]
    steps:
      - uses: actions/checkout@v5
      - run: make test

  on_managed:
    runs-on: warp-ubuntu-latest-x64-4x
    steps:
      - uses: actions/checkout@v5
      - run: make test

The three jobs run the same commands. The on_self_hosted job fails to start when no registered runner carries all three labels, and it queues rather than erroring, which is the most common way a self-hosted migration stalls. The on_managed job needs no fleet of your own, because the runner is provisioned when the job is queued and destroyed when it finishes.

Who patches the image

GitHub-hosted images ship a fixed toolchain set that GitHub updates on its own schedule. Your workflow inherits both the contents and the timing of those updates.

A self-hosted fleet moves that job to you. Every preinstalled compiler, SDK, and CLI is yours to install, version, and keep current, and every image update is a change you have to roll out across the fleet without breaking in-flight workflows. Teams usually underestimate this line item, because it is continuous work rather than a one-time build.

Managed runners keep the images on the vendor. WarpBuild publishes runner images and the software they carry in the cloud runners documentation, and the workflow change is the label in runs-on.

Who pays for idle capacity

This is where the cost models separate. GitHub-hosted and managed runners bill for the minutes a job actually runs. A self-hosted instance you keep warm bills for wall-clock time, so its real cost per busy minute depends on how full the machine is.

The arithmetic is short. Let P be the hourly price of the instance from your own cloud bill and u be utilization, meaning busy minutes divided by wall-clock minutes. The effective cost of one busy hour on that instance is P / u. A warp-ubuntu-latest-x64-4x runner at $0.008 per minute costs $0.48 per hour of job time (pricing page, checked on 2026-08-13), so a self-hosted 4 vCPU instance lands below that rate once u clears P / 0.48.

Your instance price per hourUtilization needed to match $0.48 per busy hour
$0.1021 percent
$0.1531 percent
$0.2042 percent
$0.2552 percent
$0.3063 percent

Substitute the hourly price from your own cloud bill. Two things the table leaves out usually decide the answer. The first is engineering time: image builds, autoscaling, runner upgrades, and the on-call load when the fleet stalls mid-release. The second is the shape of the demand. Pull request traffic arrives in bursts during working hours and drops overnight, so a fleet sized for the peak sits near empty for most of the month, and a fleet sized for the average queues jobs at the peak.

Isolation and machine reuse

A GitHub-hosted runner is a fresh virtual machine per job that is destroyed when the job ends, so nothing a job writes to disk reaches the next one.

A self-hosted runner reuses its machine by default. State from a previous job stays on disk, which is useful for warm caches and dangerous for credentials, and it is the reason ephemeral registration exists. Container-based fleets built on ARC share a host kernel across pods, and the WarpBuild security documentation is explicit that WarpBuild instead runs each job in its own virtual machine, created on demand and destroyed after the build, with an encrypted storage volume per runner.

The ephemeral runner definition covers the registration modes in detail, and the self-hosted runner definition covers the neutral terminology.

Where the machine sits on the network

Self-hosted runners run inside your network, so a job can reach a private database, an internal package registry, or a service behind a VPN with no extra plumbing. That placement is the strongest argument for operating your own fleet, and it also covers data residency requirements that a shared multi-tenant fleet cannot answer.

GitHub-hosted runners run in GitHub's network. Reaching a private endpoint from them means opening a path inward or tunneling outward.

Managed runners can be either. WarpBuild runs jobs in its own cloud by default and in your cloud account under bring-your-own-cloud, which keeps the network boundary yours while the vendor keeps the operational work. Hosted runners versus bring-your-own-cloud runners walks through the tradeoff, and can GitHub Actions runners run in my own cloud answers the narrower question.

Public repositories and Dependabot

Two GitHub behaviors apply to every self-hosted registration, including managed runners, and they surprise teams during migrations.

Public repositories are blocked first. GitHub's runner group policy serves private repositories by default, so a workflow on a public repository never reaches your registered runners until an organization owner changes the policy. GitHub's own guidance recommends keeping self-hosted runners on private repositories, because a fork can propose a workflow change that then executes on your machine (GitHub runner access documentation, checked on 2026-08-13).

For WarpBuild the fix is one checkbox. Open the organization's default runner group settings at https://github.com/organizations/[YOUR_ORG]/settings/actions/runner-groups/1 and check Allow public repositories. The steps and the GitHub Enterprise variant are in the public repositories documentation, and the isolation reasoning is in the security documentation.

Dependabot is the second behavior. Dependabot update jobs run on GitHub-hosted runners unless an owner turns on the Dependabot on self-hosted runners setting and provides a runner carrying the dependabot label. The setting applies to private repositories, and when no runner with that label is available the update jobs queue indefinitely rather than failing (GitHub Dependabot documentation, checked on 2026-08-13). Moving your build jobs to self-hosted or managed runners therefore leaves the dependency update path where it was, which is the correct default for most teams.

What each shape costs to run

GitHub publishes its per-minute prices in the GitHub Actions billing reference. A self-hosted fleet has no per-minute price at all, and its cost is the cloud bill plus the operating time modeled above.

Managed runner rates are published per label. The table below is taken from the pricing page, checked on 2026-08-13.

Runner labelPlatformvCPURAMPrice per minute
warp-ubuntu-latest-x64-2xLinux x6428 GB$0.004
warp-ubuntu-latest-x64-4xLinux x64416 GB$0.008
warp-ubuntu-latest-x64-8xLinux x64832 GB$0.016
warp-ubuntu-latest-arm64-4xLinux ARM64416 GB$0.006
warp-macos-latest-arm64-6xmacOS622 GB$0.08
warp-windows-latest-x64-4xWindows416 GB$0.016

Every cost number on this page carries its source and the date it was checked, and the same figures appear on the pricing page.

Is a managed runner a self-hosted runner?

To GitHub, yes. A managed runner registers through the same self-hosted registration path and appears under your organization's runner settings, so GitHub applies the self-hosted rules to it: runner group policies, the public repository setting, and the Dependabot behavior. The difference is who operates the machine underneath that registration. The self-hosted runner definition holds the neutral terminology.

Are GitHub-hosted runners ephemeral?

Yes. GitHub creates a fresh virtual machine for each job and destroys it when the job finishes. Self-hosted runners are ephemeral only when you configure them that way, which is why a long-lived self-hosted fleet accumulates state across jobs unless you rebuild the machine between them. The ephemeral runner definition covers the registration modes and what each one leaves behind.

Can self-hosted runners be used on public repositories?

Only after a runner group allows it. By default a runner group serves private repositories, so jobs on public repositories never reach your self-hosted runners until an organization owner changes that policy. GitHub also recommends against the combination, because a fork can propose a workflow change that then runs on your machine. The public repositories documentation has the exact setting and the isolation model that makes the combination safe on WarpBuild runners.

Should I run hosted runners or runners in my own cloud?

Hosted runners remove the capacity and image work and bill per job minute. Runners in your own cloud keep every job inside your account and network boundary, which matters for data residency and for reaching private services, and they move compute onto your cloud bill. Hosted runners versus bring-your-own-cloud runners works through the decision with the operating costs attached.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.