Paravirtualized NixOS domU on non-NixOS Xen using pygrub
Just a quick note on how to use paravirtualized NixOS VMs on a Debian Xen with the original NixOS kernel using pygrub. Most people seem to run NixOS VMs on a NixOS Xen (and can hence use the HV's kernel) or use full virtualization (looking at you, Proxmox) with a standalone grub in the VM. It took me quite some time to figure this out on Debian so I thought I'd share.
Objectives:
- Have a fully contained NixOS (grub config, kernel and all) that can be run on any Xen HV with pygrub
- Use NixOS's grub config to generate kernel and initrd updates
- Have Xen's pygrub parse NixOS's grub config to allow kernel selection and boot NixOS
- Have everything stable and automated, no manual steps after the initial setup
Here's what I came up with:
We start with an install-only HVM xen config to be able to boot from the NixOS ISO image. The relevant Xen config parts are:
type = "hvm"
device_model_version = "qemu-xen"
bios = "seabios"
disk = [
'phy:/dev/vgNIXOS/nixos-boot,xvda,rw',
'phy:/dev/vgNIXOS/nixos-root,xvdb,rw',
'file:/path/to/latest-nixos-minimal-x86_64-linux.iso,hdc:cdrom,r',
]
boot = "dc"
The boot LV can be as small as 500 MiB, the root LV should be at least 5 GiB. Now, start this domU with the -c parameter to get a console. It'll boot into the NixOS installer. Once that's ready, we prepare the NixOS install:
mkfs.ext4 /dev/xvda -L NIXBOOT mkfs.ext4 /dev/xvdb -L NIXROOT mount /dev/disk/by-label/NIXROOT /mnt mkdir -p /mnt/boot mount /dev/disk/by-label/NIXBOOT /mnt/boot nixos-generate-config --root /mntNow comes the actual NixOS config. In hardware-configuration.nix, we need to specify the Xen modules and the block devices:
boot.initrd.availableKernelModules = [ "ata_piix" "sr_mod" "xen_blkfront" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/xvdb";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/xvda";
fsType = "ext4";
};
and in configuration.nix we configure the boot loader:
boot.loader.grub = {
enable = true;
device = "nodev"; # don't actually install grub
configurationLimit = 3;
fsIdentifier = "provided"; # disable uuid identifiers
extraConfig = ''
set root=(hd0)
''; # force kernel + initrd load from boot partition
};
The combination of device = "nodev", fsIdentifier = "provided" and extraConfig makes sure that NixOS generates a grub config that allows pygrub to boot the correct kernel and initrd.
Now start the NixOS installation:
nixos-installOnce this has finished, you can poweroff the domU. We're now switching to a paravirtualized domU for optimal performance. Create a new Xen config file with the relevant parts being:
bootloader = 'pygrub'
disk = [
'phy:/dev/vgNIXOS/nixos-boot,xvda,rw',
'phy:/dev/vgNIXOS/nixos-root,xvdb,rw',
]
This should now nicely boot into your NixOS system. Have fun!
a 2025 daduke production. all rights reserved.
