AWS Builder Center

Ditch the Bare Metal - Android Emulation on EC2 with Nested Virtualization

Lessons learned on KVM, Android Emulation, and how to take advantage of nested virtualization in EC2 instances to save money when compared to bare metal.

Introduction

If you run Android CI pipelines at scale, you know the pain: bare metal instances are the only way to get the KVM acceleration your emulators need, and that comes with a real cost premium. Bare metal instances serve one customer at a time. You pay for the full physical host whether you run one emulator or twenty. That changed in February 2026 when AWS announced nested virtualization support on C8i, M8i, and R8i instances . You can now run KVM-accelerated Android emulators on standard virtualized EC2 instances for the first time.
This post covers what KVM is, why emulators need it, why bare metal was previously required, and how nested virtualization changes that. I also walk through getting Android emulation working on a virtualized EC2 instance from scratch.

KVM: What It Is, and Why Emulators Need It

KVM (Kernel-based Virtual Machine) is a full virtualization solution for Linux on x86 hardware. It exposes processor virtualization extensions (Intel VT-x or AMD-V) directly to guest workloads. KVM lets the Linux kernel act as a hypervisor. A virtual machine runs guest code directly on the host processor instead of relying on software-based instruction translation.
For Android emulators, that distinction determines whether the emulator is usable in CI. Without KVM, the emulator translates every ARM instruction to x86 in software, block by block. This makes boot times and test execution far too slow for CI pipelines. With KVM, the emulator runs guest code at near-native speed.

Why That Required Bare Metal

Standard EC2 instances are virtualized: multiple instances share the same physical host, managed by the AWS Nitro System . The Nitro hypervisor sits between your instance and the hardware, so it does not expose processor virtualization extensions to the guest by default. This blocked KVM on standard instances. Teams that needed KVM used bare metal instances, where the Nitro hypervisor handles security and networking but exposes the full CPU. Bare metal works, but the dedicated tenancy model makes it expensive at pipeline scale.

What Nested Virtualization Is

Nested virtualization, announced in February 2026, works by having the Nitro System pass processor virtualization extensions through to your EC2 instance. This creates three layers:
  • L0: The physical AWS infrastructure and Nitro hypervisor
  • L1: Your EC2 instance, now running its own hypervisor (KVM)
  • L2: One or more virtual machines or Android emulators running inside that instance
Your EC2 instance uses KVM the same way a bare metal host does, without the dedicated tenancy cost. AWS supports nested virtualization on C8i, M8i, and R8i instances.

What I Learned Getting This to Work

Enable nested virtualization by checking the "Nested virtualization" flag under "Advanced Details"  when launching a new instance. If your instance already runs without the flag, stop it first, then modify the instance attribute to enable it before restarting.
Once the instance runs, verify KVM is available:
1
2
3
4
5
6
7
sudo apt-get update && sudo apt-get install -y cpu-checker

ls -l /dev/kvm

grep -c vmx /proc/cpuinfo

sudo kvm-ok
You want to see /dev/kvm exist, a vmx count greater than 0, and kvm-ok print "KVM acceleration can be used".

Install Java and Headless Emulator System Libraries

I tested on ami-0d13e2317a7e75c95 (ubuntu-noble-26.04-amd64-server-20260421), the latest Ubuntu LTS image at publication time. Install Java and the system libraries the headless emulator requires:
1
2
3
4
5
6
sudo apt-get install -y openjdk-17-jdk

# headless emulator dependencies (the libs QEMU needs even with -no-audio)
sudo apt-get install -y libpulse0 libnss3 libxcomposite1 libxcursor1 libxi6 \
libxtst6 libasound2t64 libgl1 libglu1-mesa libxdamage1 libxrandr2 \
libxrender1 libxfixes3 libxkbcommon0 unzip
Each package links to the Ubuntu package index for reference:

Grant the KVM Group

kvm-ok passes under sudo, but the emulator runs as your user and needs group access to /dev/kvm. On a fresh instance, the kvm group has no members by default. Add your user explicitly:
1
sudo gpasswd -a "$USER" kvm
If you are connected to the instance when you run this, log out and back in before the group change takes effect.

Install the Android Command-Line Tools

1
2
3
4
5
6
7
8
export ANDROID_HOME=$HOME/android-sdk
mkdir -p $ANDROID_HOME/cmdline-tools
cd /tmp
# check developer.android.com/studio for the current build number if this 404s
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip commandlinetools-linux-*.zip -d $ANDROID_HOME/cmdline-tools
# the tools REQUIRE this 'latest/' nesting or sdkmanager can't find its root
mv $ANDROID_HOME/cmdline-tools/cmdline-tools $ANDROID_HOME/cmdline-tools/latest
Persist the environment variables so they survive reconnects:
1
2
3
echo 'export ANDROID_HOME=$HOME/android-sdk' >> ~/.bashrc
echo 'export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator' >> ~/.bashrc
source ~/.bashrc

Install SDK Components and Create the AVD

1
2
3
4
yes | sdkmanager --licenses
sdkmanager "platform-tools" "emulator" "system-images;android-34;google_apis;x86_64"
avdmanager create avd -n bench -k "system-images;android-34;google_apis;x86_64" -d pixel_6
# answer "no" to the custom hardware profile prompt

Test that One Emulator Boots on KVM

1
2
3
4
5
6
emulator -avd bench -read-only -no-window -no-audio -no-metrics -no-boot-anim \
-gpu swiftshader_indirect -no-snapshot &
adb wait-for-device
until [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ]; do sleep 2; done
echo BOOTED
adb shell getprop ro.kernel.qemu # 1 = inside emulator

Cost Comparison: Nested Virtualization vs. Bare Metal

The per-vCPU rate is identical for virtualized and bare metal instances. The difference: bare metal forces you to buy the entire physical host regardless of how many emulators you run. All prices below are US East (N. Virginia), Linux, on-demand as of June 2026.
InstanceTypevCPUMemory$/hr~/month (730h)$/hr per vCPU
c8i.12xlargeVirtualized4896 GiB$2.2490$1,642$0.0469
c8i.metal-48xlBare metal192384 GiB$8.9962$6,567$0.0469
m8i.12xlargeVirtualized48192 GiB$2.5402$1,854$0.0529
m8i.metal-48xlBare metal192768 GiB$10.1606$7,417$0.0529
r8i.12xlargeVirtualized48384 GiB$3.3341$2,434$0.0695
r8i.metal-48xlBare metal1921,536 GiB$13.3363$9,736$0.0695
m5zn.metal (prev. gen)Bare metal48192 GiB$3.9641$2,894$0.0826
A c8i.4xlarge costs $0.75/hr and handles a typical emulator workload. That is 5x cheaper than m5zn.metal ($3.96/hr), a common pre-nested-virt choice, and 12x cheaper than an equivalent 8th-gen bare metal host.
Virtualized instances also unlock purchasing options that bare metal does not support. You can run emulator fleets on Spot Instances for up to 70% savings on interruptible CI jobs. Savings Plans let you commit to steady-state capacity at a lower rate. You can match instance size to the number of parallel emulators your pipeline runs instead of paying for a fixed physical host.
Pair Auto Scaling  with EC2 Warm Pools  to handle scale-out latency. Emulator instances take time to boot and reach a ready state. Pre-initializing a pool of stopped instances lets your pipeline scale out without a full cold start on every run. Save a custom AMI with all the changes above.

Conclusion

For the first time, KVM-accelerated Android emulators run on standard EC2 instances. That ends the bare metal tax. You no longer buy a full physical host that serves one job at a time. You run a virtualized instance sized to the emulators you actually launch, and you back bursts with Spot capacity at up to 70% off. The shift is bigger than a lower hourly rate. Right-sizing, Spot, Savings Plans, and Auto Scaling were all off the table on bare metal. Nested virtualization puts every one of them back in play for Android CI. The steps above boot a single emulator on KVM on a C8I instance. To get from there to a production fleet, do three things: bake a custom AMI from the instance you just configured, put it behind an Auto Scaling group, and front that group with a warm pool so scale-out skips the cold start. Same emulators, a fraction of the bill.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article