How Do I Change the Runner in a GitHub Actions Workflow?
Change the label on the runs-on line. Swapping ubuntu-latest for warp-ubuntu-latest-x64-8x is the only edit a managed runner needs in a workflow file.
Last verified:
Change the label on the runs-on line, and change nothing else. A managed GitHub Actions runner is selected entirely by label, so replacing ubuntu-latest with warp-ubuntu-latest-x64-8x moves the job to an 8 vCPU machine while every step, action pin, secret reference, and service container in the file stays where it is.
Answer
runs-on is the only field in a job definition that decides which machine executes the job. GitHub reads the value, looks for a runner advertising that label, and hands the job to the first one that matches. Steps never see the label, so nothing downstream of runs-on needs an edit.
Here is the swap on a normal Node.js workflow. Before:
name: ci
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/After:
name: ci
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: warp-ubuntu-latest-x64-8x
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/One line differs. The Linux x64 images carry the same tooling as GitHub-hosted runners, so actions/setup-node, actions/cache, service containers, and artifact upload behave the same way (cloud runners documentation, checked on 2026-08-13).
Before that label resolves, the account has to exist and the GitHub app has to be installed. The WarpBuild GitHub bot cannot be installed from the GitHub Marketplace: sign up first, then install it from the WarpBuild dashboard and grant it the repositories you want to run on (quick start documentation). Until the bot has access to a repository, a warp- label in that repository finds no runner and the job queues.
The same one-line change works whichever platform the job needs. Per-minute rates for every label are on the pricing page.
Detail
The four segments of a runner label
Every cloud runner label follows the same shape: warp-<platform>-<image>-<architecture>-<size>.
| Segment | Values | In warp-ubuntu-latest-x64-8x |
|---|---|---|
| Prefix | warp- routes the job to WarpBuild | warp- |
| Platform | ubuntu, macos, windows | ubuntu |
| Image | latest or a dated release such as 2404, 2604, 2204, 26, 15, 14, 2022, 2025, 2025-vs2026 | latest |
| Architecture | x64 or arm64 | x64 |
| Size | 2x through 32x on Linux and Windows, 6x or 12x on macOS | 8x |
The size digit is the vCPU count. A 2x Linux runner is 2 vCPU with 8 GB of RAM, an 8x is 8 vCPU with 32 GB, and a 32x is 32 vCPU with 128 GB. Per-minute price scales with the size, so the arithmetic below stays predictable across the catalog.
Two segments are constrained by the platform. macOS runners are ARM64 only, so a macOS label always reads arm64. Windows runners are x86-64 only, so a Windows label always reads x64. Linux carries both architectures.
What the latest aliases point at
latest is an alias that tracks the current default image for its platform, and each one has a dated twin you can pin instead. Every row comes from the cloud runners documentation, checked on 2026-08-13.
| Alias label | Resolves to | Underlying image | Per minute |
|---|---|---|---|
| warp-ubuntu-latest-x64-8x | warp-ubuntu-2404-x64-8x | Ubuntu 24.04 | $0.016 |
| warp-ubuntu-latest-arm64-8x | warp-ubuntu-2404-arm64-8x | Ubuntu 24.04 | $0.012 |
| warp-macos-latest-arm64-6x | warp-macos-15-arm64-6x | macOS 15 | $0.08 |
| warp-windows-latest-x64-8x | warp-windows-2022-x64-8x | Windows Server 2022 | $0.032 |
The macOS latest tag was switched to macOS 15 to stay in sync with GitHub's macos-latest tag. Newer images ship under their own dated labels rather than moving the alias, which is why Ubuntu 26.04 is warp-ubuntu-2604-x64-8x and macOS 26 is warp-macos-26-arm64-6x. The macOS 26 image also ships the Xcode 27.0 SDKs and simulator runtimes while GitHub's upstream macOS 27 runner image is in beta, and a dedicated macOS 27 image follows once that image is released.
Use latest while you are testing the swap. Pin the dated label on workflows that must not move when the alias points at a new release, the same way you would pin ubuntu-24.04 instead of ubuntu-latest.
Modifiers append to the label with a semicolon
Features that change how the machine boots or what it can reach are requested in the same string, separated by semicolons and written as key=value. No spaces.
| Modifier | What it does | Where it works |
|---|---|---|
snapshot.enabled=true | Boots from the base image and lets WarpBuilds/snapshot-save capture the machine mid-workflow | Cloud Ubuntu runners only |
snapshot.key=<alias> | Boots from the snapshot stored under that alias, falling back to the base image when none exists yet | Cloud Ubuntu runners only |
nested-virtualization.enabled=true | Provisions the runner on hardware that exposes /dev/kvm to the guest | Cloud Linux x86-64 only |
network.name=<config> | Joins the runner to your Tailscale tailnet using a Network Addon Configuration | Ubuntu x64, Ubuntu ARM64, macOS ARM64, Windows x64 |
The platform limits matter because an unsupported modifier is silently ignored rather than failing the job. Snapshot labels on BYOC, Windows, or macOS runners do nothing and the job runs normally without snapshots (snapshot runners documentation). The nested virtualization label on ARM64, Windows, or macOS behaves the same way, and on BYOC runner sets nested virtualization is enabled automatically when every selected instance type supports it, with no label required (nested virtualization documentation). A workflow that quietly loses hardware acceleration is harder to spot than one that fails, so check the platform column before you add a modifier.
Modifiers chain:
jobs:
build:
runs-on: warp-ubuntu-latest-x64-8x;network.name=production-tailnet;snapshot.key=api-deps
steps:
- uses: actions/checkout@v5
- run: npm ci
- run: npm run build
instrumentation-tests:
runs-on: warp-ubuntu-latest-x64-4x;nested-virtualization.enabled=true
steps:
- uses: actions/checkout@v5
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- run: ./gradlew connectedCheckTwo operational notes on snapshots. Snapshots are deleted after 15 days, and /tmp does not persist because the directory is cleaned on reboot.
What the swap costs
Hold the minutes constant and the label change is pure list-price arithmetic. Take a repository that burns 20,000 runner minutes a month. WarpBuild rates come from the cloud runners documentation. GitHub rates come from the GitHub Actions billing reference, checked on 2026-08-13.
Label in runs-on | Shape | Per minute | 20,000 minutes |
|---|---|---|---|
| ubuntu-latest, GitHub-hosted on a private repository | 2 vCPU, 8 GB | $0.006 | $120.00 |
| warp-ubuntu-latest-x64-2x | 2 vCPU, 8 GB | $0.004 | $80.00 |
| the 8-core Linux larger runner, GitHub-hosted | 8 vCPU, 32 GB | $0.022 | $440.00 |
| warp-ubuntu-latest-x64-8x | 8 vCPU, 32 GB | $0.016 | $320.00 |
At the same shape, the swap moves the monthly bill from $120.00 to $80.00. At 8 vCPU against the GitHub-hosted 8-core larger runner, it moves from $440.00 to $320.00 for the same minutes.
The interesting number is the one that connects size to bill. Moving from ubuntu-latest to warp-ubuntu-latest-x64-8x multiplies the per-minute rate from $0.006 to $0.016, so the monthly bill is unchanged when the job finishes in 37.5 percent of its original wall clock and falls below the old bill anywhere under that mark. Time your longest job on both labels for a week before you decide the size, because the answer depends on how much of the job actually parallelizes.
Rolling the change out without touching every file
A repository with thirty workflow files does not want thirty commits and thirty reverts. Read the label from a repository variable instead:
jobs:
build:
runs-on: ${{ vars.LINUX_RUNNER || 'ubuntu-latest' }}Set LINUX_RUNNER to warp-ubuntu-latest-x64-8x under repository or organization variables and every workflow that reads it moves at once. Clear the variable and every workflow falls back to ubuntu-latest on the next run. The fallback also keeps forks working, since forks do not inherit the variable.
The same expression form handles a branch-conditional label, which is how a snapshot workflow separates the run that creates a snapshot from the runs that consume it:
jobs:
build:
runs-on: >-
${{ github.ref == 'refs/heads/main'
&& 'warp-ubuntu-latest-x64-8x;snapshot.enabled=true'
|| 'warp-ubuntu-latest-x64-8x;snapshot.key=api-deps' }}Quote the label when it comes from an expression. A bare runs-on value with semicolons needs no quotes.
Related Questions
Can one matrix mix GitHub-hosted labels and warp- labels?
Yes. Put the label string in the matrix and point runs-on at the matrix value. Each leg resolves its own label independently, so a rollout can keep one leg on ubuntu-latest while another runs on warp-ubuntu-latest-x64-8x and you compare the two on the same commit.
jobs:
test:
strategy:
fail-fast: false
matrix:
include:
- name: github-hosted
runner: ubuntu-latest
- name: warpbuild
runner: warp-ubuntu-latest-x64-8x
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v5
- run: npm ci
- run: npm testfail-fast: false keeps both legs reporting. More on running two runner pools side by side is in mixing GitHub-hosted and self-hosted runners.
What label do BYOC runners use?
BYOC runners use their Runner ID, which is the runner name prefixed with warp-custom-, for example warp-custom-ci-stack-runner. The exact string is in the Runner ID column on the custom runners page in the dashboard, and it goes into runs-on the same way a cloud label does (BYOC documentation). Snapshot modifiers are ignored on BYOC runners; nested virtualization needs no modifier there.
How do I roll back to GitHub-hosted runners?
Change the label back. Nothing else in the workflow file depends on the runner, so a revert commit restores the previous behavior on the next run. Teams that expect to move back and forth read the label from a repository variable as shown above, which turns a rollback into one variable edit. The runner label glossary entry covers how GitHub resolves a label to a machine.
Should I use the latest labels or the dated ones?
Use latest while you are testing so you pick up the current default image, then pin the dated label such as warp-ubuntu-2404-x64-8x on workflows that must not move when the alias points at a new release. The full label list with sizes and prices is in the Linux x64 runner catalog.
Start by swapping one job, confirm the steps pass unchanged as described in whether faster runners need workflow changes, then price your own minutes against the table on the pricing page.
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.