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 labelWarpBuild labelShapeGitHub per minuteWarpBuild per minuteList price difference
ubuntu-latestwarp-ubuntu-latest-x64-2x2 vCPU, 8 GB$0.006$0.00433 percent lower list price
4-core Linux larger runnerwarp-ubuntu-latest-x64-4x4 vCPU, 16 GB$0.012$0.00833 percent lower list price
8-core Linux larger runnerwarp-ubuntu-latest-x64-8x8 vCPU, 32 GB$0.022$0.01627 percent lower list price
16-core Linux larger runnerwarp-ubuntu-latest-x64-16x16 vCPU, 64 GB$0.042$0.03224 percent lower list price
32-core Linux larger runnerwarp-ubuntu-latest-x64-32x32 vCPU, 128 GB$0.082$0.06422 percent lower list price
ubuntu-24.04-armwarp-ubuntu-latest-arm64-2x2 vCPU, 8 GB$0.005$0.00340 percent lower list price
4-core Windows larger runnerwarp-windows-latest-x64-4x4 vCPU, 16 GB$0.022$0.01627 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 actionWarpBuild forkWhat it caches
actions/setup-nodeWarpBuilds/setup-node@v6npm, yarn, and pnpm dependencies
actions/setup-pythonWarpBuilds/setup-python@v6pip, pipenv, and poetry dependencies
actions/setup-goWarpBuilds/setup-go@v6Go modules and build outputs
actions/setup-javaWarpBuilds/setup-java@v5Maven, Gradle, and sbt dependencies
actions/setup-dotnetWarpBuilds/setup-dotnet@v4NuGet global packages
ruby/setup-rubyWarpBuilds/[email protected]Gems installed by bundle install
Swatinem/rust-cacheWarpBuilds/rust-cache@v2Cargo registry, git deps, and target dir
gradle/actionsWarpBuilds/gradle-actions/setup-gradle@v5Gradle User Home
jdx/mise-actionWarpBuilds/mise-action@v2Tools installed by mise
goto-bus-stop/setup-zigWarpBuilds/setup-zig@v2Downloaded 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-4x at $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 -q

fail-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.

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.