QEMU KVM - Create Arch Linux VM for Audio E2E Testing

This guide has two parts: first, setting up QEMU with KVM acceleration on your host machine. Second, creating and configuring Arch Linux VM images for testing audio applications.

I use QEMU because it's fast with KVM and trivial to run from the command line — no GUI, no complexity, just scriptable VMs.

Table of Contents #

Installation and setting up QEMU #

Install QEMU:

sudo pacman -S qemu-full

1. CPU support — check with:

LC_ALL=C lscpu | grep -i virtualization
# Should show: VT-x (Intel) or AMD-V (AMD)

2. Kernel modules — load and persist:

sudo modprobe kvm
sudo modprobe kvm_intel # Intel
# OR
sudo modprobe kvm_amd # AMD

# Persist after reboot:
echo "kvm" | sudo tee /etc/modules-load.d/kvm.conf
echo "kvm_amd" | sudo tee -a /etc/modules-load.d/kvm.conf # for AMD
#echo "kvm_intel" | sudo tee -a /etc/modules-load.d/kvm.conf # adjust for INTEL

3. User permissions — add yourself to the group:

sudo usermod -aG kvm $USER
# Log out and back in for group to apply

4. Verify it works:

ls -la /dev/kvm          # Should exist and be owned by kvm group
qemu-system-x86_64 -accel kvm -hda /dev/null # Should start without "falling back to tcg" warning

Creating Arch Linux VM for Audio E2E Testing #

1. Create Base Disk Image #

qemu-img create -f qcow2 arch-base.qcow2 10G

Download the Arch ISO (check archlinux.org/download for current version):

# HTTP download from geo mirror
curl -O https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso

# OR use BitTorrent (recommended by Arch, helps reduce mirror load):
# curl -O https://archlinux.org/releng/releases/latest/torrent/
# Then open the .torrent file with your client (transmission, qbittorrent, etc.)

Verify the ISO signature (optional but recommended):

# Download signature and checksums
curl -O https://geo.mirror.pkgbuild.com/iso/latest/sha256sums.txt
curl -O https://geo.mirror.pkgbuild.com/iso/latest/sha256sums.txt.sig

# Verify checksum matches
sha256sum -c sha256sums.txt --ignore-missing

# Verify PGP signature (requires archlinux-keyring installed)
gpg --verify sha256sums.txt.sig sha256sums.txt

2. Boot the ISO and Install Arch #

Start the VM with the ISO mounted for installation:

qemu-system-x86_64 \
-accel kvm \
-m 2G \
-cdrom archlinux-x86_64.iso \
-hda arch-base.qcow2 \
-boot d

This opens a normal window where you can run through the Arch installer. Follow the Arch installation guide with these choices:

#SettingSelectionNotes
0Disk10GB btrfszstd compression, flat layout (no subvolumes), skip LVM
1SwapSkip2GB RAM is sufficient
2BootloaderLimineModern, handles btrfs natively
3KernelStandard linuxNot LTS or Zen
4Userpr / prCreate user pr with password pr
5ProfileMinimalNo desktop environment needed
6NetworkCopy ISO configurationsystemd-networkd with DHCP
7AudioPipeWireEnable PipeWire user services after first boot
8FirewallUFWDisable it in post install; VM is NAT isolated

Additional packages to install:

# Base system
base base-devel linux linux-firmware

# Testing essentials
openssh neovim curl git wget btop jq zip unzip alsa-utils libpulse pulsemixer openssh

3. Post Install Configuration #

Before freezing the base image, configure the system for automated testing. Boot the newly installed system (not the ISO) and run:

qemu-system-x86_64 -accel kvm \
-m 2G \
-hda arch-base.qcow2 \
-audiodev pa,id=snd0 \
-device intel-hda \
-device hda-duplex,audiodev=snd0 \
-nic user,hostfwd=tcp::2222-:22
  1. Enable SSH server
sudo systemctl enable --now sshd
  1. Disable Firewall
sudo ufw disable
systemctl enable serial-getty@ttyS0.service
  1. Optional: passwordless sudo for automated testing
echo "pr ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/pr
  1. Verify audio is working
pactl info | grep "Server Name"  # Should show "PulseAudio (on PipeWire)"
# Enable PipeWire if it's not working
# systemctl --user enable --now pipewire pipewire-pulse wireplumber
  1. Reduce bootloader timeout (Limine)

Edit the Limine configuration to speed up boot:

sudo sed -i 's/timeout:.*/timeout: 0/' /boot/limine/limine.conf
  1. Power off to freeze the base image
poweroff

4. Freeze Base Image and Create Test Snapshot #

After configuration, shut down and protect the base image from accidental writes. QCOW2 snapshots reference specific block offsets in the backing file — modifying the base after creating snapshots corrupts the chain.

chmod 444 arch-base.qcow2

Create a derived snapshot for testing:

qemu-img create -f qcow2 -F qcow2 -b arch-base.qcow2 arch-snapshot.qcow2

5. Launch for Testing #

Testing runs headless with SSH access. Start the VM without display and connect via forwarded port:

# Start the VM headless
qemu-system-x86_64 -accel kvm -m 2G -hda arch-snapshot.qcow2 \
-audiodev pa,id=snd0 -device intel-hda -device hda-duplex,audiodev=snd0 \
-display none -serial stdio \
-nic user,hostfwd=tcp::2222-:22

# From another terminal, SSH into the VM
ssh -p 2222 pr@localhost

The serial console (-serial stdio) shows kernel messages and boot progress. Once booted, use SSH for interactive work.

Resetting Test State #

Snapshots are copy-on-write. To reset to clean state, delete and recreate:

rm arch-snapshot.qcow2
qemu-img create -f qcow2 -F qcow2 -b arch-base.qcow2 arch-snapshot.qcow2

Quick QEMU Command Reference #

Once you have a working VM image, here's a quick template to launch it headless with audio and SSH support:

qemu-system-x86_64 \
-accel kvm \
-m 2G \
-hda arch-snapshot.qcow2 \
-audiodev pa,id=snd0 \ # PulseAudio backend
-device intel-hda -device hda-duplex,audiodev=snd0 \ # Audio device
-display none \ # No GUI
-serial stdio \ # Serial console for boot logs
-nic user,hostfwd=tcp::2222-:22 # SSH on port 2222

Connect via SSH: ssh -p 2222 pr@localhost

The -accel kvm flag is what makes it fast. Without it, QEMU silently falls back to TCG.

Published