Do I Need to Rewrite My Workflow for Faster Runners?
No. The runs-on label is the only required change, because WarpBuild runner images carry the same tooling as GitHub-hosted runners and steps run as written.
Last verified:
No. The runs-on label is the only line you are required to change, because the runner images carry the same tooling as GitHub-hosted runners and every step, action reference, expression, and secret in the file is evaluated the same way. Three documented differences deserve a check before a repository-wide rollout: the Ubuntu 24.04 ARM64 work directory, cache actions on Windows, and Docker or nested virtualization availability per platform (cloud runners documentation, checked on 2026-08-13).
Answer
A GitHub Actions job picks its machine with one line. Everything above and below that line belongs to the workflow parser and the runner agent: steps, uses references, with inputs, if conditions, ${{ }} expressions, secrets, OIDC token requests, service containers, artifacts, strategy.matrix, concurrency, permissions, reusable workflow calls, and composite actions. None of that is aware of who supplies the hardware.
So the migration is a label swap, plus an optional second swap if you want dependency caching served by WarpBuild Cache.
name: build-and-test
on:
push:
branches: [main]
pull_request:
jobs:
test:
- runs-on: ubuntu-latest
+ runs-on: warp-ubuntu-latest-x64-4x
steps:
- uses: actions/checkout@v4
- - uses: actions/setup-node@v4
+ - uses: WarpBuilds/setup-node@v6
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/Two lines moved. The first is the whole migration. The second is optional: actions/setup-node@v4 runs fine on a WarpBuild runner, and the WarpBuilds/setup-node@v6 fork accepts the same inputs and produces the same outputs while routing the npm cache to WarpBuild Cache (setup actions documentation).
Which label replaces which
Every WarpBuild rate below comes from the cloud runners documentation. Every GitHub rate comes from the GitHub Actions billing reference, checked on 2026-08-13.
| GitHub-hosted label | WarpBuild label | Shape | GitHub per minute | WarpBuild per minute | List price difference |
|---|---|---|---|---|---|
| ubuntu-latest | warp-ubuntu-latest-x64-2x | 2 vCPU, 8 GB | $0.006 | $0.004 | 33 percent lower list price |
| 4-core Linux larger runner | warp-ubuntu-latest-x64-4x | 4 vCPU, 16 GB | $0.012 | $0.008 | 33 percent lower list price |
| 8-core Linux larger runner | warp-ubuntu-latest-x64-8x | 8 vCPU, 32 GB | $0.022 | $0.016 | 27 percent lower list price |
| 16-core Linux larger runner | warp-ubuntu-latest-x64-16x | 16 vCPU, 64 GB | $0.042 | $0.032 | 24 percent lower list price |
| 32-core Linux larger runner | warp-ubuntu-latest-x64-32x | 32 vCPU, 128 GB | $0.082 | $0.064 | 22 percent lower list price |
| ubuntu-24.04-arm | warp-ubuntu-latest-arm64-2x | 2 vCPU, 8 GB | $0.005 | $0.003 | 40 percent lower list price |
| 4-core Windows larger runner | warp-windows-latest-x64-4x | 4 vCPU, 16 GB | $0.022 | $0.016 | 27 percent lower list price |
The ubuntu-latest and ubuntu-24.04-arm rows quote GitHub's private-repository rates, which are the rates paying teams run against. macOS is the one row where the shapes differ: warp-macos-latest-arm64-6x is 6 vCPU and 22 GB at $0.08 per minute, against $0.102 per minute for the largest GitHub-hosted macOS ARM64 runner at 5 vCPU and 14 GB, so that comparison carries one more vCPU and 8 GB more RAM at the lower rate.
The table holds job minutes constant, so it measures list price alone. Per-size arithmetic for every label lives on the pricing page.
Detail
What carries over with no edits
The Linux x86-64 and Windows x86-64 runner images have the same tooling installed as GitHub-hosted runners (cloud runners documentation). Language toolchains, package managers, Docker, the AWS and Azure CLIs, and the rest of the preinstalled set are present under the same paths, so a step that shells out to a binary finds it.
The Ubuntu 24.04 ARM64 images are compatible with GitHub's Ubuntu 24.04 ARM64 runners, and the published package list is at actions/partner-runner-images. Read that list before you assume a specific language version, because ARM64 images and x86-64 images do not always ship the same set.
Runner storage is ephemeral and is deleted when the runner terminates, which matches GitHub-hosted behavior. Anything a job needs to outlive itself goes to an artifact, a cache, or a registry.
The three things worth checking
The Ubuntu 24.04 ARM64 work directory. The arm64 images for Ubuntu 24.04 set the work dir to /runner/_work, which differs from GitHub's /home/runner/work/ for the same instance (cloud runners documentation). Any step with a literal /home/runner/work/... path breaks on that platform. Use ${{ github.workspace }} in YAML and $GITHUB_WORKSPACE in shell steps, and audit Docker -v mounts, coverage upload paths, and cache path globs for hardcoded prefixes. x86-64 Linux, macOS, and Windows are unaffected.
Cache actions on Windows. WarpBuild caching is not supported on Windows runners (caching documentation). Windows jobs keep actions/cache@v4 and the upstream setup-* actions. On Linux and macOS you may switch to WarpBuilds/cache@v1, which is compatible with actions/cache@v4 and accepts the same key, path, and restore-keys inputs. A mixed-platform matrix can therefore carry two cache steps gated by runner.os, or simply leave every leg on the GitHub cache until the Linux legs are proven.
Two cache behaviors change the mental model slightly. Entries are scoped to the key, the version, and the branch, and the version hash covers the compression tool plus the cached paths, so a cache saved on warp-macos-14-arm64-6x cannot restore on warp-ubuntu-latest-x64-4x. Entries expire after 7 days of last use. Cache storage bills at $0.20 per GB-month and $0.0001 per operation on hosted runners, and is free on BYOC.
Docker and nested virtualization per platform. Linux x86-64 runners run Docker and can request /dev/kvm through the nested-virtualization.enabled=true dynamic label, which is what Android emulator jobs need. ARM64 Linux runners do not support nested virtualization, so those jobs stay on an x86-64 label. macOS runners do not support nested virtualization and cannot run Docker at all, so a macOS job that shells out to docker build needs to move to a Linux runner or to a remote Docker builder. All three facts are in the cloud runners documentation.
The optional cache swap, in full
Ten forks cover the common toolchains, and each one is a one line change with the same inputs as its upstream (setup actions documentation).
| Upstream action | WarpBuild fork | What it caches |
|---|---|---|
| actions/setup-node | WarpBuilds/setup-node@v6 | npm, yarn, and pnpm dependencies |
| actions/setup-python | WarpBuilds/setup-python@v6 | pip, pipenv, and poetry dependencies |
| actions/setup-go | WarpBuilds/setup-go@v6 | Go modules and build outputs |
| actions/setup-java | WarpBuilds/setup-java@v5 | Maven, Gradle, and sbt dependencies |
| actions/setup-dotnet | WarpBuilds/setup-dotnet@v4 | NuGet global packages |
| ruby/setup-ruby | WarpBuilds/[email protected] | Gems installed by bundle install |
| Swatinem/rust-cache | WarpBuilds/rust-cache@v2 | Cargo registry, git deps, and target dir |
| gradle/actions | WarpBuilds/gradle-actions/setup-gradle@v5 | Gradle User Home |
| jdx/mise-action | WarpBuilds/mise-action@v2 | Tools installed by mise |
| goto-bus-stop/setup-zig | WarpBuilds/setup-zig@v2 | Downloaded Zig compilers |
For a hand-rolled cache step the change is the same shape:
- - uses: actions/cache@v4
+ - uses: WarpBuilds/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-These forks pick up WarpBuild Cache automatically when they run on a WarpBuild runner, with no extra configuration. On a GitHub-hosted runner they fall back to the GitHub cache, so a mixed matrix does not need branching logic.
What the swap is worth on one repository
A service repository merges enough pull requests to queue 2,000 jobs a month at 5 minutes each on a 4 vCPU Linux runner. That is 10,000 billable minutes.
warp-ubuntu-latest-x64-4xat $0.008 per minute: 10,000 x $0.008 = $80.00 per month.- GitHub 4-core Linux larger runner at $0.012 per minute: 10,000 x $0.012 = $120.00 per month.
- Difference: $40.00 per month, $480.00 over twelve months, which is 33 percent lower list price (GitHub Actions billing reference, checked on 2026-08-13).
- The $10 in signup credits covers the first $10 of that bill, so month one lands at $70.00 out of pocket at this volume.
The arithmetic assumes identical job minutes on both sides, so it sets a floor on the difference.
Rolling out one job at a time
The label lives in a matrix like any other value, so a single workflow can run both sides and let you read the difference from the Actions tab.
name: runner-comparison
on:
pull_request:
jobs:
test:
strategy:
fail-fast: false
matrix:
runner:
- ubuntu-latest
- warp-ubuntu-latest-x64-4x
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: pytest -qfail-fast: false keeps the GitHub-hosted leg running when the new leg fails, which is what you want while you are proving the swap. Once the numbers look right, delete the matrix and keep one label.
For a repository where the label should be switchable without a commit, put it in a repository variable and reference it as runs-on: ${{ vars.DEFAULT_RUNNER }}. Rollback becomes a settings change instead of a pull request, which is useful during a first week on new hardware.
Optional features can be added later as a step, label modifier, or profile. None is a prerequisite for the runner-label swap described above.
Related Questions
How do I roll back if a job fails on the faster runner?
Change the runs-on line back to the GitHub-hosted label and re-run. No other line in the workflow file depends on the runner, so the revert is a one line commit, or a repository variable you flip without touching the file. The runner label reference covers where the label can live in a workflow.
Can one matrix run some legs on GitHub-hosted runners and some on WarpBuild runners?
Yes. Put both labels in the matrix and read them with runs-on: ${{ matrix.runner }}, as in the comparison workflow above. Both legs execute the same steps, which is the cleanest way to compare wall clock and per-minute cost on your own repository. Mixing runner types inside one workflow is a supported pattern, covered in mixing GitHub-hosted and self-hosted runners.
Do I have to change my caching setup?
No. actions/cache@v4 keeps working on WarpBuild runners. WarpBuilds/cache@v1 is a drop-in replacement for it, and the WarpBuilds setup forks use WarpBuild Cache automatically on WarpBuild runners. WarpBuild caching is not supported on Windows runners, so Windows jobs stay on the GitHub cache. The setup actions documentation lists every fork and its cache inputs.
What does it cost to try the swap on one workflow?
Price your own workload against the pricing page before you widen the rollout.
Start with one job on a label from the Linux x64 runner catalog, keep the GitHub-hosted leg in the matrix for a week, and work through the rest of the pipeline with the guide to speeding up GitHub Actions workflows.
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.