KVM
KVM is the Linux kernel virtualization interface, exposed as the device /dev/kvm, that lets a process run a guest with hardware acceleration on the host CPU.
KVM, short for Kernel-based Virtual Machine, is the virtualization interface built into the Linux kernel and exposed to user space as the character device /dev/kvm. A process opens that device, asks the kernel for a virtual machine and its virtual CPUs, and the guest's instructions then execute on the physical CPU through the hardware virtualization extensions instead of being interpreted one at a time in software.
Two facts explain almost every KVM question that starts in a GitHub Actions log. The device has to exist in the environment where the job runs, and the user running the job has to be able to open it. Programs that want hardware acceleration check both, and quietly continue in software emulation when either check fails.
Definition
KVM is a set of kernel modules: kvm.ko for the architecture independent core, plus kvm-intel.ko or kvm-amd.ko for the vendor specific extensions, Intel VT-x and AMD-V. Loading them turns the running Linux kernel into a hypervisor and creates the misc device /dev/kvm, character major 10, minor 232.
KVM supplies the CPU and memory half of a virtual machine. The user space program supplies the rest: the memory layout, the virtual disks and network interfaces, the firmware, and the loop that drives execution. QEMU, crosvm, cloud-hypervisor, and the Android emulator are all user space programs that reach the kernel through the same device.
The interface is a small set of ioctl calls, documented in the kernel's KVM API reference:
| Call | Issued on | Result |
|---|---|---|
KVM_GET_API_VERSION | /dev/kvm | Version handshake before anything else |
KVM_CREATE_VM | /dev/kvm | A file descriptor representing one virtual machine |
KVM_SET_USER_MEMORY_REGION | The VM descriptor | Maps a region of the process's address space as guest RAM |
KVM_CREATE_VCPU | The VM descriptor | A file descriptor representing one virtual CPU |
KVM_RUN | A vCPU descriptor | Runs guest code on the physical CPU until a VM exit |
KVM_RUN is where the acceleration lives. The kernel switches the physical CPU into guest mode and the guest's ordinary instructions execute at hardware speed. When the guest touches something the hypervisor has to mediate, such as a device register or a halt instruction, the CPU exits back to the kernel, which returns from KVM_RUN with an exit reason such as KVM_EXIT_IO, KVM_EXIT_MMIO, or KVM_EXIT_HLT in the shared kvm_run structure. The user space program services that exit and calls KVM_RUN again.
This differs from the translation work described under QEMU emulation. Translation reads foreign architecture instructions and rewrites them for the host CPU. KVM leaves the guest instructions alone, because they are already native to the CPU underneath, so its job is confined to mode switching, memory mapping, and interrupt delivery.
The three states a program can find
A program probing for acceleration is really testing two conditions, and the failure signatures differ:
State of /dev/kvm | What an open call returns | What the program usually does |
|---|---|---|
| Device absent, no virtualization extensions in this environment | ENOENT, no such file or directory | Falls back to software emulation |
| Device present, permissions deny the caller | EACCES, permission denied | Falls back to software emulation |
| Device present and openable | A file descriptor | Runs the guest with hardware acceleration |
The permission case catches people out because the device is right there in the listing. Distributions ship a udev rule that creates the node as crw-rw---- root:kvm, so a process whose user is outside the kvm group gets EACCES from a device that plainly exists.
Nesting is the other half. A GitHub Actions runner is already a virtual machine, so the kernel inside it can only load the KVM modules when the hypervisor below it exposes the virtualization extensions to that guest. That capability is nested virtualization, and where it is unavailable, no configuration inside the job can create /dev/kvm.
Example
The Android emulator is the workload most GitHub Actions users meet KVM through. This job prints the state of the device, applies the standard permission rule, and then runs instrumentation tests:
name: instrumentation
on:
pull_request:
jobs:
emulator:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Report KVM availability
run: |
if [ -e /dev/kvm ]; then
ls -l /dev/kvm
id -nG
else
echo "no /dev/kvm in this environment"
fi
- name: Allow the job user to open /dev/kvm
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 connectedCheckWhen the device is openable, the report step prints the node and the emulator boots with hardware acceleration:
$ ls -l /dev/kvm
crw-rw-rw- 1 root kvm 10, 232 Aug 13 09:14 /dev/kvmWhen it is not, the emulator does not fail the step. It reports the probe result and continues in software:
ProbeKVM: This user doesn't have permissions to use KVM (/dev/kvm).
Disabling Linux hardware acceleration.
emulator: ... -accel off ...That -accel off on the emulator command line is the signal worth grepping for, because the job still passes and only the wall clock time gives the fallback away. The android-emulator-runner README documents the permission step as a requirement on Linux runners, and GitHub's changelog on hardware accelerated Android virtualization records which hosted runner classes gained the underlying capability, both checked on 2026-08-13.
Any other KVM dependent workload reads the same way. A libvirt or QEMU step that boots a full guest, a kernel test that runs under a virtual machine, and an emulator all start by opening /dev/kvm, and all of them degrade to software execution rather than erroring when the open fails.
Related Terms
- Nested virtualization, the layer that puts /dev/kvm inside a GitHub Actions runner: what the hypervisor below has to expose before a guest kernel can load the KVM modules at all.
- GitHub Actions runners that expose /dev/kvm to the job: which runner classes support the capability and the label shape that turns it on.
- Can GitHub Actions run an Android emulator?: the end to end answer for emulator jobs, including the permission step and sizing.
- QEMU emulation: the software translation path used when the guest architecture differs from the host.
- Linux KVM API documentation: the upstream reference for the device, the ioctls, and the exit reasons.
- WarpBuild nested virtualization documentation: the support matrix and configuration reference.
- WarpBuild cloud runners documentation: the runner catalog with sizes, images, and architectures.
- WarpBuild pricing: per minute rates by runner type.
FAQ
What does /dev/kvm actually do?
It is the entry point to the kernel's virtualization interface. A process opens the device, issues KVM_CREATE_VM and KVM_CREATE_VCPU to get file descriptors for a virtual machine and its virtual CPUs, then calls KVM_RUN to execute guest instructions on the physical CPU through the hardware virtualization extensions. Control returns to the process on a VM exit, which is how device access and other privileged operations get handled in user space.
Why is /dev/kvm missing inside a GitHub Actions job?
A hosted runner is itself a virtual machine, so the device appears in the guest only when the layer underneath exposes the CPU virtualization extensions to that guest. That is nested virtualization. Without it the kvm modules have no hardware to program, the device node is never created, and an open call returns ENOENT.
What is the difference between KVM and QEMU?
KVM is a kernel interface that runs same architecture guest code directly on the host CPU. QEMU is a user space program that emulates machines and devices, and it can either translate foreign architecture instructions in software or hand same architecture guest code to KVM for hardware acceleration. Programs such as the Android emulator use KVM the same way, through /dev/kvm.
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.