GitHub Actions Runners with KVM and Nested Virtualization
Nested virtualization on WarpBuild Linux x64 GitHub Actions runners exposes /dev/kvm via one runs-on label. Support matrix, rates, and Android emulator setup.
Last verified:
Nested virtualization on WarpBuild runners for GitHub Actions is a Linux x64 feature. Adding nested-virtualization.enabled=true to the runs-on label places the job on hardware that exposes the virtualization extensions to the guest, which makes /dev/kvm available inside the runner, and on BYOC runner sets the same capability turns on automatically when every instance type selected for the set supports it (nested virtualization documentation).
This page is the reference for that feature: which runner classes carry it, what the sizes that carry it cost per minute, the exact label syntax, and the permissions step Android emulator workflows need before the emulator uses hardware acceleration. Specifications and rates come from the WarpBuild cloud runners documentation and were re-checked on 2026-08-13.
Catalog
WarpBuild provides Linux x64, Linux ARM64, macOS, and Windows runners. Nested virtualization is available on the Linux x64 class, on cloud runners through a label and on BYOC runner sets through the instance types you select.
| Runner class | Nested virtualization | How it is enabled |
|---|---|---|
| Cloud: Linux x86-64 | Yes | nested-virtualization.enabled=true on the runs-on label |
| Cloud: Linux ARM64 | No | Label is ignored |
| Cloud: Windows | No | Label is ignored |
| Cloud: macOS | No | Label is ignored |
| BYOC: AWS (Linux x86-64) | Yes | Automatic when all selected instance types support it |
| BYOC: GCP (Linux x86-64) | Yes | Automatic when all selected instance types support it |
| BYOC: Azure (Linux x86-64) | Yes | Automatic on supported VM sizes |
| BYOC: ARM64 and Windows | No | Unavailable |
Two operational consequences follow from that matrix. A nested-virtualization.enabled=true label on an unsupported cloud runner class is silently ignored and the job runs normally without /dev/kvm, so a misplaced label produces a passing job on software emulation rather than a failure you can see. And a BYOC runner set that mixes supporting and non-supporting instance types for availability gets nested virtualization on none of its jobs, because the capability is withheld from the whole set rather than varying between jobs inside it.
Every Linux x64 size carries the feature. The five sizes and their specifications:
| Size | vCPU | Memory | Storage | Rate |
|---|---|---|---|---|
| 2x | 2 | 8GB | 150GB SSD | $0.004/minute |
| 4x | 4 | 16GB | 150GB SSD | $0.008/minute |
| 8x | 8 | 32GB | 150GB SSD | $0.016/minute |
| 16x | 16 | 64GB | 150GB SSD | $0.032/minute |
| 32x | 32 | 128GB | 150GB SSD | $0.064/minute |
Three image families carry those five sizes, for fifteen Linux x64 labels in total: warp-ubuntu-latest-x64-<size> on Ubuntu 24.04, aliased as warp-ubuntu-2404-x64-<size>; warp-ubuntu-2604-x64-<size> on Ubuntu 26.04; and warp-ubuntu-2204-x64-<size> on Ubuntu 22.04. The rate follows the size and stays the same across the three images, so moving from Ubuntu 22.04 to Ubuntu 26.04 changes the toolchain without changing the bill.
Runner storage is ephemeral and is deleted when the runner terminates. For emulator work that means an AVD created during a job is gone at the end of it, and the boot cost belongs in the job time you budget.
macOS runners do not support nested virtualization and cannot run Docker, so iOS work uses the simulator runtimes on the macOS image rather than a KVM guest.
Pricing
Pricing is purely usage based. There is no base subscription fee, no platform fee, and no seat fee. Nested virtualization has no separate line on the rate card, so a job that sets the label bills the per-minute rate of the size it ran on.
| Size | Rate | 12-minute job | 10,560 minutes a month |
|---|---|---|---|
| 2x | $0.004/minute | $0.048 | $42.24 |
| 4x | $0.008/minute | $0.096 | $84.48 |
| 8x | $0.016/minute | $0.192 | $168.96 |
| 16x | $0.032/minute | $0.384 | $337.92 |
| 32x | $0.064/minute | $0.768 | $675.84 |
The monthly column models one instrumentation suite that takes 12 minutes and fires 40 times a day across 22 working days, which is 10,560 runner minutes.
Against the GitHub-hosted 8-core Linux shape
warp-ubuntu-latest-x64-8x (8 vCPU, 32 GB) costs $0.016 per minute against $0.022 per minute for the 8-core Linux larger runner (8 vCPU, 32 GB): 27 percent lower list price. GitHub list price checked on 2026-08-13 against the GitHub Actions minute multipliers reference.
The arithmetic, written out: ($0.022 - $0.016) / $0.022 = 0.2727, which rounds to 27 percent. Over the 10,560 minute month above that is $168.96 against $232.32, a difference of $63.36. The comparison holds size for size and feature for feature, because GitHub's Linux larger hosted runners also expose hardware acceleration for Android virtualization (GitHub changelog, February 2023).
Signup includes $10 free credits, which covers 625 minutes on the 8x size or 2,500 minutes on the 2x size. Every rate quoted here also appears on the pricing page.
Configuration
WarpBuild reads runner options from the runs-on string, separated by semicolons. One option is all this feature needs:
jobs:
instrumentation-tests:
runs-on: warp-ubuntu-latest-x64-4x;nested-virtualization.enabled=trueOnce /dev/kvm exists on the runner, its default device permissions of crw-rw---- root:kvm still stop the runner user opening it, so an Android emulator job needs a udev rule first:
- 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=kvmThat step is required on all GitHub and GitHub-compatible runners and is documented in the android-emulator-runner README. Skipping it gives you a green build on the wrong engine: the action's ProbeKVM check fails, the log carries a line about the user lacking permission to use /dev/kvm followed by Disabling Linux hardware acceleration., and the emulator command line includes -accel off. Searching an already-passing job log for -accel off is the quickest way to find out whether it ever had acceleration.
A complete workflow, with a check that fails loudly when the device node is missing:
name: android-instrumentation
on:
pull_request:
branches: [main]
jobs:
instrumentation-tests:
runs-on: warp-ubuntu-latest-x64-8x;nested-virtualization.enabled=true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Confirm the KVM device node is present
run: ls -l /dev/kvm
- 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
- uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
arch: x86_64
script: ./gradlew connectedCheckOn BYOC the label comes out and the instance list does the work. Pick instance types that all support nested virtualization when you build the runner set, because a single unsupported type in the list turns the capability off for every job the set runs.
When a supported label still reports no device node, the Action Debugger pauses the workflow and opens an SSH session on the runner, so you can inspect the guest instead of adding print statements and pushing again.
For choosing a size for an emulator matrix, see sizing runners for Android builds. For the whole workflow shape around instrumentation tests, see Android emulator tests on GitHub Actions. For the term itself, see nested virtualization, and for the short answer on whether emulators run at all, see can GitHub Actions run an Android emulator.
FAQ
Which WarpBuild runners support nested virtualization and KVM?
Linux x64. On cloud runners you add nested-virtualization.enabled=true to the runs-on label, and on BYOC runner sets it is enabled automatically when every instance type selected for the set supports it. Linux ARM64, Windows, and macOS runner classes do not support it, and the label is ignored on them.
Does enabling nested virtualization cost extra?
No. Nested virtualization has no separate line on the rate card, so a job bills the per-minute rate of the size it ran on, from $0.004 a minute on the 2x Linux x64 size to $0.064 a minute on the 32x size.
Why does the Android emulator still run without hardware acceleration on a KVM runner?
Device permissions. The device node defaults to crw-rw---- root:kvm, which the runner user cannot open, so the emulator action reports that the user lacks permission to use KVM and launches the emulator with -accel off. Add the udev rule step that writes 99-kvm4all.rules before the emulator step.
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.