Documentation · Runners
Runner labels
The exact strings a runs-on accepts, the machine each one gets you, and why a misspelled label or one paired with self-hosted is refused, with the fix named on the job page.
A job runs on our runners when its runs-on: names one of our labels. There is nothing else to configure.
jobs:
build:
runs-on: ghwarp-2vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v4
- run: make test| label | vCPU | memory | disk |
|---|---|---|---|
ghwarp-2vcpu-ubuntu-2404 | 2 | 8 GB | 80 GB |
ghwarp-4vcpu-ubuntu-2404 | 4 | 16 GB | 80 GB |
ghwarp-8vcpu-ubuntu-2404 | 8 | 32 GB | 160 GB |
ghwarp-16vcpu-ubuntu-2404 | 16 | 64 GB | 750 GB |
All four are Ubuntu 24.04 on x64. A ghwarp- label that is not one we sell is refused and explained on the job page. What each costs is on the pricing page.
Matching is exact, against the labels we sell and nothing else, and the label goes in runs-on on its own.
Case does not matter. GitHub compares
runs-onlabels case-insensitively, and so do we:GHWarp-2vCPU-Ubuntu-2404asks for the same runner. Surrounding whitespace is trimmed for the same reason.Our label, and nothing beside it.
runs-on: [self-hosted, ghwarp-2vcpu-ubuntu-2404]is the usual self-hosted habit, and it does not work here — not because we object toself-hosted, but because of how GitHub assigns jobs. GitHub hands a job only to a runner that carries every label the job names, and the runner we start for your job carries exactly one: our label. A job that also namesself-hosted,linux,x64, or a second label of ours can never be handed to it. We refuse such a job the moment it arrives and say so on its job page, naming the label to remove. Nothing runs and nothing is charged.A label that starts with
ghwarp-and is not one we sell is refused too. A typo —ubuntu-2204whereubuntu-2404was meant, say — is addressed to us, so we do not leave it hanging: the job appears on your Runners page as refused, with the label you wrote and a link back here. We never guess which runner you meant — the wrong guess would bill you for a machine you did not ask for. Fix the spelling and push again.A label that does not start with
ghwarp-is not ours.ubuntu-latest,windows-latest, orself-hostedon its own are addressed to GitHub's runners or to your own fleet, and we leave those jobs alone entirely.
There are no aliases. ubuntu-latest is GitHub's own label and always runs on GitHub's runners; see moving from GitHub-hosted runners for what to put in its place.
Until the run is cancelled, GitHub's own side goes on showing a refused job as Waiting for a runner, because nothing tells GitHub that we refused that one job. Nobody is coming. The refusal on your job page is the earlier, honest signal — if a job sits Queued on GitHub and there is nothing on your Runners page, the label does not start with ghwarp- and the job was never addressed to us.
We ask GitHub to cancel the run, but only when every job on it was one we refused. GitHub cancels runs rather than individual jobs, so that is the only lever available for ending a wait nobody will answer — and using it on a run that still has work in it would kill that work. Before cancelling we ask GitHub what else the run contains, including jobs that are not addressed to us at all. So:
A refused job beside other jobs of ours that we accepted. We leave the run alone. Your other jobs run to completion, and the refused one sits on Waiting for a runner until GitHub gives up on it, or until you cancel the run yourself. Nothing extra is charged; the refused job never started.
A refused job beside jobs on GitHub's own runners. Also left alone. A
ubuntu-latestjob is invisible to us — it never reaches our systems — so we ask GitHub directly rather than assuming the run is ours to end. This is the usual shape when you move one job at a time, and moving one job at a time is safe.A run where every job was one we refused. We ask GitHub to cancel it, so it stops waiting. We cannot always: cancelling needs the
actions: writepermission on your installation of our GitHub App, and if the account owner has not accepted that upgrade the run waits for GitHub's own timeout instead. Everything else about your account works normally.Anything we are unsure about is left alone. If GitHub cannot be reached, or answers something we cannot make sense of, we do not cancel. The cost is a refused job that waits for GitHub's timeout; the alternative risks ending a build that was working.
The refusal itself is never the surprise — it is on the job page within seconds, naming the label to change.
Each job runs in its own virtual machine, which no other job uses and which is destroyed when the job ends. Nothing is reused between jobs, including between two jobs in the same workflow, except a sticky disk if you have asked for one.
Inside it you have passwordless sudo, Docker, and the Ubuntu 24.04 image; the runner image lists what is installed. actions/checkout, actions/cache, actions/upload-artifact and the rest of the standard actions work unmodified, because from GitHub's point of view this is an ordinary self-hosted runner.
What is absent, and why:
No
/dev/kvm. Nested virtualisation is not available, so an Android emulator or a nested hypervisor will not start. Docker containers do not need it and are unaffected.No GPU.
No ARM, Windows or macOS. A workflow that needs one of those should keep those jobs on
ubuntu-latest,windows-latestormacos-latest— a matrix can mix ours and GitHub's freely.
Limits has how long a job may run and how many run at once.
Nothing requires a whole workflow to move. runs-on is per job, so this is valid and does what it looks like:
jobs:
test:
runs-on: ghwarp-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v4
- run: make test
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- run: make testA matrix works the same way — put the label in the matrix and the jobs split across both fleets.
Moving one job at a time is safe. If the job you moved names a label we refuse, the refusal is recorded against that job and the GitHub-hosted jobs beside it are unaffected — we check what else is on the run before asking GitHub to cancel anything, and a run carrying work that is not ours is left alone.