self-host →

Nested virtualization

When to use Linux KVM or Windows Hyper-V inside RunsOn runners, and the EC2 constraints that matter.

Nested virtualization lets a CI runner start another virtual machine inside the EC2 instance. The common cases are Linux KVM workloads, Android emulator acceleration, VM-based integration tests, and Windows Hyper-V workloads.

Usage#

Flex#

On Linux, nested-virt exposes KVM to the job so VM-based tests and accelerated emulators can run:

jobs:
kvm:
runs-on: runs-on=${{ github.run_id }}/family=c8i.large/image=ubuntu24-full-x64/nested-virt
steps:
- name: Verify KVM
run: |
test -c /dev/kvm
sudo apt-get update -qq
sudo apt-get install -y cpu-checker
sudo kvm-ok

On Windows, the same idea enables Hyper-V features and Hyper-V-isolated containers:

jobs:
hyperv:
runs-on: runs-on=${{ github.run_id }}/family=m8i.large/image=windows25-full-x64/nested-virt
steps:
- shell: pwsh
run: |
Import-Module Hyper-V
Get-WindowsFeature Hyper-V,Hyper-V-PowerShell,Containers
$Image = "mcr.microsoft.com/windows/nanoserver:ltsc2025"
docker run --isolation=hyperv $Image cmd /c ver

See nested-virt and Windows runners for full Flex syntax.

Fleet#

For Fleet, nested virtualization is configured in Terraform. On a current stack the nested-enabled launch templates already exist, so you only mark the runner definitions that should launch with nested virtualization:

...
runners = {
deploy-nested-virt = {
family = ["m8i.large"]
image = "windows25-full-x64"
nested-virt = true
}
}

See Runner fleets for the full runner-catalog configuration.

Constraints#

Nested virtualization is a host capability, not a package you can install after boot. The runner must launch on an EC2 family that supports it, and the stack must include the right launch templates.

Practical constraints:

  • x64 only — arm64 EC2 families do not support nested virtualization;
  • Linux uses KVM; Windows uses Hyper-V;
  • supported EC2 families only — current x64 Nitro families that expose nested virtualization;
  • requires a current RunsOn stack — existing stacks must be upgraded to include the nested-enabled launch templates before nested-virt jobs can run, otherwise the launch fails.

Flex vs Fleet#

Same hardware requirement; the difference is who decides which jobs get the capability.

AspectFlexFleet
How a job enables itnested-virt job labelTarget a runner fleet that the platform team has pre-approved for nested-virt
Per-workflow override
Where the policy livesWorkflow author chooses at runtimeTerraform-owned runner fleet (family, image, IAM, network)
Supported familiesSame x64 Nitro familiesSame x64 Nitro families

When not to use it#

Do not enable nested virtualization for ordinary builds, tests, or Docker workloads. It narrows instance selection and can increase launch pressure if you also require a specific family, image, region, or Spot capacity pool.