VM Testing
This document covers testing Keystone configurations in QEMU/KVM virtual machines, including setup requirements, known issues, and troubleshooting.
Overview
Keystone uses libvirt-managed QEMU/KVM VMs for testing NixOS configurations before deploying to physical hardware. This provides a safe, reproducible environment for validating:
- Disk encryption and ZFS configuration
- Secure Boot setup and key enrollment
- TPM2 integration
- Desktop environment (Hyprland) functionality
- System module integration
VM Requirements
Host System
- NixOS with libvirtd enabled
- User must be in
libvirtdgroup - QEMU with UEFI/OVMF firmware support
- Sufficient resources (4GB+ RAM, 2+ vCPUs recommended)
NixOS Configuration
virtualisation.libvirtd.enable = true;
users.users.<youruser>.extraGroups = [ "libvirtd" ];Essential Configuration: qemu-guest.nix Profile
Critical: All VM configurations MUST import the qemu-guest.nix profile to function properly.
Why qemu-guest.nix is Required
The qemu-guest.nix profile provides:
- Complete virtio driver stack - Including graphics-related drivers
- QEMU guest agent - Better host-guest communication
- VM-specific optimizations - Performance tuning for virtualization
- Graphics backend support - DRM/KMS drivers for VM graphics
Without this profile, you may experience:
- Missing virtio drivers
- Graphics initialization failures
- Poor VM performance
- Device detection issues
How to Add qemu-guest.nix
In your VM configuration (e.g., vms/test-hyprland/configuration.nix):
{
config,
pkgs,
lib,
modulesPath, # Required for profiles
...
}: {
# Import QEMU guest profile
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
# Ensure complete kernel module set
boot.initrd.availableKernelModules = [
"ahci"
"xhci_pci"
"virtio_pci"
"virtio_blk"
"virtio_net"
"virtio_scsi"
"sr_mod"
];
# Rest of your configuration...
}VM Management with bin/virtual-machine
The bin/virtual-machine script handles VM lifecycle:
# Create and start VM
./bin/virtual-machine --name keystone-test-vm --start
# Create with custom resources
./bin/virtual-machine --name test --memory 8192 --vcpus 4 --disk-size 50 --start
# Post-installation: snapshot, remove ISO, reboot
./bin/virtual-machine --post-install-reboot keystone-test-vm
# Complete reset
./bin/virtual-machine --reset keystone-test-vmVM Features
- UEFI Secure Boot in Setup Mode (no pre-enrolled keys)
- TPM 2.0 emulation for testing TPM features
- Serial console for debugging
- SPICE graphics for remote viewing
- Static IP (192.168.100.99 on keystone-net)
Connection Methods
# Graphical display
remote-viewer $(virsh domdisplay keystone-test-vm)
# Serial console
virsh console keystone-test-vm
# SSH (after installation)
./bin/test-vm-ssh
./bin/test-vm-ssh "systemctl status"Graphics Configuration for VMs
Working Configuration (2025-11-08)
QXL is the working graphics driver for Hyprland in QEMU/KVM VMs:
<graphics type='spice' autoport='yes'>
<listen type='address' address='127.0.0.1'/>
</graphics>
<video>
<model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1'/>
</video>While QXL is primarily designed for SPICE protocol and has some limitations with Wayland, it provides sufficient DRM/KMS support for Hyprland to function in VM environments.
Why virtio Doesn't Work
virtio-gpu without GL acceleration is incompatible with Hyprland:
<video>
<model type='virtio'/>
</video>Result: ❌ Failed - Missing virgl 3D acceleration, Hyprland cannot initialize EGL
Explanation:
- Plain virtio-gpu provides basic 2D framebuffer support
- Hyprland requires OpenGL ES 2.0 minimum for compositing
- Without virgl/GL acceleration, virtio-gpu cannot provide the required OpenGL context
- Attempting to use virtio results in:
libEGL warning: egl: failed to create dri2 screen
QXL vs virtio:
- QXL provides sufficient DRM/KMS despite being designed for SPICE
- virtio requires GL acceleration (virgl) for Wayland compositors
- Since virgl/GL is unavailable (NVIDIA host incompatibility), QXL is the only viable option
Hardware GL Acceleration Investigation
Extensive testing was conducted to enable hardware-accelerated graphics (OpenGL/virgl) for better performance. All hardware acceleration approaches failed due to NVIDIA driver incompatibilities with libvirt.
Attempted GL Acceleration Solutions
3. SDL with GL (incorrect XML syntax)
<graphics type='sdl' display=':0' gl='yes'/>
<video>
<model type='virtio-vga-gl'/>
</video>Result: ❌ Failed - Attribute syntax incorrect (should be child element)
Error:
The display backend does not have OpenGL support enabled
It can be enabled with '-display BACKEND,gl=on'4. SDL with GL (correct XML syntax)
<graphics type='sdl' display=':0'>
<gl enable='yes'/>
</graphics>
<video>
<model type='virtio-vga-gl'/>
</video>Result: ❌ Failed - Same error as above
Analysis: libvirt not properly translating XML to QEMU command-line arguments
5. SPICE with GL and explicit rendernode
<graphics type='spice'>
<listen type='none'/>
<image compression='off'/>
<gl enable='yes' rendernode='/dev/dri/by-path/pci-0000:0a:00.0-render'/>
</graphics>
<video>
<model type='virtio' heads='1' primary='yes'>
<acceleration accel3d='yes'/>
</model>
</video>Result: ❌ Failed
Error:
qemu-system-x86_64: egl: eglInitialize failed: EGL_NOT_INITIALIZED
qemu-system-x86_64: egl: render node init failedRoot Cause Analysis
NixOS Issue #164436: "libvirt: openGL does not work with Nvidia GPUs"
The fundamental problem is an incompatibility between:
- NVIDIA proprietary drivers
- libvirt's EGL initialization
- QEMU in
qemu:///systemmode
This is a known, unresolved issue on NixOS when using NVIDIA GPUs with libvirt OpenGL.
Web Research Findings
Extensive web research confirmed:
- Multiple NixOS users reporting identical
eglInitialize failederrors - NVIDIA proprietary drivers have compatibility issues with QEMU/libvirt EGL
- SDL GL child element syntax (
<gl enable='yes'/>) is correct per libvirt docs (added 2018) - SPICE GL with explicit rendernode is the recommended workaround on Arch Linux
- None of these workarounds resolve the NVIDIA + libvirt + NixOS issue
References
- NixOS GitHub #164436 - libvirt OpenGL not working with NVIDIA GPUs
- NixOS Discourse: QEMU 3D acceleration error
- Arch Linux Forum: virgl with libvirt
- libvirt SDL OpenGL support patches (2018)
Software Rendering Fallback (Optional)
If hardware acceleration is unavailable, Hyprland can use software rendering (llvmpipe):
In VM Configuration
Keep basic virtio graphics (no GL):
<graphics type='spice' autoport='yes'>
<listen type='address' address='127.0.0.1'/>
</graphics>
<video>
<model type='virtio' heads='1' primary='yes'/>
</video>In Guest NixOS Configuration
Add to modules/client/desktop/hyprland.nix or system configuration:
environment.sessionVariables = {
WLR_RENDERER_ALLOW_SOFTWARE = "1";
WLR_NO_HARDWARE_CURSORS = "1";
};Trade-offs
- ✅ Works with any GPU/driver combination
- ✅ No host EGL/GL dependencies
- ✅ Reliable for development/testing
- ⚠️ Slower graphics performance (CPU-based rendering)
- ⚠️ Higher CPU usage during compositing
- ⚠️ Not suitable for graphics-intensive workloads
Recommendation: Only use software rendering if hardware acceleration is confirmed unavailable. With proper qemu-guest.nix configuration, basic virtio graphics should work for most testing scenarios.
Testing Procedure
1. Build ISO
./bin/build-iso --ssh-key ~/.ssh/id_ed25519.pub2. Create VM
./bin/virtual-machine --name keystone-test-vm --start3. Connect to VM
# Graphical (for installer)
remote-viewer $(virsh domdisplay keystone-test-vm)
# Serial console (for debugging)
virsh console keystone-test-vm4. Deploy Configuration
# From installer or host
nixos-anywhere --flake .#test-hyprland root@192.168.100.995. Post-Installation
# Shutdown VM
virsh shutdown keystone-test-vm
# Remove ISO and snapshot
./bin/virtual-machine --post-install-reboot keystone-test-vm6. Verify Configuration
# SSH into VM
./bin/test-vm-ssh
# Inside VM, verify:
bootctl status # Check Secure Boot status
zpool status rpool # Verify ZFS pool
systemctl status hyprland # Check desktop serviceTroubleshooting
VM Won't Start
Check OVMF firmware:
ls -la /nix/store/*-OVMF-*/FV/Verify libvirtd:
systemctl status libvirtd
virsh versionGraphics Issues
- Verify qemu-guest.nix is imported in VM configuration
- Check kernel modules are loaded:
lsmod | grep virtio - Review system logs:
journalctl -b | grep -i "drm\|egl\|opengl"
Hyprland Won't Start
Check compositor logs:
journalctl --user -u hyprlandVerify environment variables:
printenv | grep -E "WAYLAND|XDG|WLR"Test with basic Weston:
# Install weston for testing
nix-shell -p weston
weston --backend=drm-backend.soSerial Console Issues
Enable in kernel params:
boot.kernelParams = [
"console=ttyS0,115200n8"
"console=tty0"
];Connect:
virsh console keystone-test-vm
# Press Ctrl+] to exitKnown Issues
-
Hardware GL acceleration unavailable with NVIDIA host (NixOS #164436)
- No working solution as of 2025-11-08
- Software rendering fallback available but not recommended
-
SPICE GL requires local unix socket
- Cannot use GL with network-accessible SPICE (
-spice port=5900) - Only works with
listen type='none'(local only)
- Cannot use GL with network-accessible SPICE (
-
VM performance with encryption
- ZFS native encryption + LUKS adds overhead
- Use adequate vCPU/memory allocation
- Consider snapshot-based testing to avoid repeated installations
Best Practices
- Always import qemu-guest.nix in VM configurations
- Use descriptive VM names for multiple test scenarios
- Snapshot after successful installation for quick rollback
- Test Secure Boot before production deployment
- Verify TPM functionality if using TPM features
- Use serial console for debugging boot issues
- Keep ISO up to date with latest configuration changes
Future Improvements
Potential areas for enhancement:
-
Alternative virtualization platforms
- Test with AMD GPU hosts
- Evaluate qemu:///session vs qemu:///system
- Consider non-NVIDIA solutions for GL acceleration
-
Testing automation
- Automated test suite for VM configurations
- CI/CD integration for configuration validation
- Snapshot-based rapid testing
-
Graphics alternatives
- Monitor virglrenderer/Venus developments
- Track NixOS GitHub #164436 for NVIDIA fixes
- Evaluate GTK display backend as SDL alternative