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

SegmentValuesIn warp-ubuntu-latest-x64-8x
Prefixwarp- routes the job to WarpBuildwarp-
Platformubuntu, macos, windowsubuntu
Imagelatest or a dated release such as 2404, 2604, 2204, 26, 15, 14, 2022, 2025, 2025-vs2026latest
Architecturex64 or arm64x64
Size2x through 32x on Linux and Windows, 6x or 12x on macOS8x

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 labelResolves toUnderlying imagePer minute
warp-ubuntu-latest-x64-8xwarp-ubuntu-2404-x64-8xUbuntu 24.04$0.016
warp-ubuntu-latest-arm64-8xwarp-ubuntu-2404-arm64-8xUbuntu 24.04$0.012
warp-macos-latest-arm64-6xwarp-macos-15-arm64-6xmacOS 15$0.08
warp-windows-latest-x64-8xwarp-windows-2022-x64-8xWindows 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.

ModifierWhat it doesWhere it works
snapshot.enabled=trueBoots from the base image and lets WarpBuilds/snapshot-save capture the machine mid-workflowCloud Ubuntu runners only
snapshot.key=<alias>Boots from the snapshot stored under that alias, falling back to the base image when none exists yetCloud Ubuntu runners only
nested-virtualization.enabled=trueProvisions the runner on hardware that exposes /dev/kvm to the guestCloud Linux x86-64 only
network.name=<config>Joins the runner to your Tailscale tailnet using a Network Addon ConfigurationUbuntu 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 connectedCheck

Two 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-onShapePer minute20,000 minutes
ubuntu-latest, GitHub-hosted on a private repository2 vCPU, 8 GB$0.006$120.00
warp-ubuntu-latest-x64-2x2 vCPU, 8 GB$0.004$80.00
the 8-core Linux larger runner, GitHub-hosted8 vCPU, 32 GB$0.022$440.00
warp-ubuntu-latest-x64-8x8 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.

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 test

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