Secure Boot in a Libvirt (KVM) VM with ZFS on LUKS Impermanence
Initial VM Setup
When creating the VM in virt-manager:
-
Before clicking “Finish”, check the “Customize configuration before install” box
-
In the Overview section, change Firmware from BIOS to UEFI x86_64:
/usr/share/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2 -
Proceed with the NixOS installation as normal.
Known Issue: After running nixos-install and rebooting, the SATA CDROM
source path may be cleared. If the VM fails to boot, manually reselect the NixOS
ISO in the SATA settings and reboot.
Configure Firmware for Custom Secure Boot Keys
The default configuration uses Microsoft’s pre-enrolled keys, which won’t trust your custom-signed kernel. To enable custom key enrollment, you need to modify the VM’s XML configuration.
On your host, find your VM name:
virsh -c qemu:///system list --all
Example Output:
Id Name State
---------------------------------
- nixos-unstable shut off
- nixos shut off
Edit the VM configuration:
virsh edit nixos-unstable
Make the following changes to the <os> section:
- Change the
enrolled-keysfeature fromyestono:
<feature enabled='no' name='enrolled-keys'/>
- Delete the explicit
<loader>and<nvram>lines. These conflict with libvirt’s firmware autoselection when usingenrolled-keys='no':
<!-- DELETE THESE TWO LINES -->
<loader readonly='yes' secure='yes' type='pflash' format='qcow2'>/usr/share/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2</loader>
<nvram template='/usr/share/edk2/ovmf/OVMF_VARS_4M.secboot.qcow2' templateFormat='qcow2' format='qcow2'>/var/lib/libvirt/qemu/nvram/nixos-unstable_VARS.qcow2</nvram>
Your final <os> section should look like this:
<os firmware='efi'>
<type arch='x86_64' machine='pc-q35-10.1'>hvm</type>
<firmware>
<feature enabled='no' name='enrolled-keys'/>
<feature enabled='yes' name='secure-boot'/>
</firmware>
<boot dev='hd'/>
</os>
- Add the
<serial>tag as a best practice
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' discard='unmap'/>
<source file='/var/lib/libvirt/images/nixos-unstable-1.qcow2'/>
<serial>disk01</serial>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</disk>
- This enables commands like:
zpool import -d /dev/disk/by-id rpool
Save and exit the editor. Libvirt will now automatically create a new NVRAM file in Setup Mode (no keys enrolled).
NixOS Installation with ZFS on root with LUKS
Add the impermanence flake input.(Edited: If you use a current lanzaboote you don’t need to pin anything)
inputs = {
impermanence.url = "github:nix-community/impermanence";
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
lanzaboote = {
url = "github:nix-community/lanzaboote/v1.0.0";
inputs.nixpkgs.follows = "nixpkgs";
};
};
{ pkgs, lib, inputs, ... }: {
# configuration.nix
imports = [ inputs.lanzaboote.nixosModules.lanzaboote ];
environment.systemPackages = [ pkgs.sbctl ];
boot.loader.systemd-boot.enable = lib.mkForce false;
boot.lanzaboote = {
enable = true;
pkiBundle = "/var/lib/sbctl";
};
}
And impermanence.nix:
{ inputs, lib, ... }: {
imports = [
inputs.impermanence.nixosModules.impermanence
];
boot.initrd.postMountCommands = lib.mkAfter ''
zfs rollback -r rpool/local/root@blank
'';
environment.persistence."/persist" = {
directories = [ "/var/lib/sbctl" "/var/lib/nixos" ];
};
fileSystems."/persist" = {
device = "rpool/safe/persist";
fsType = "zfs";
neededForBoot = true;
};
}
Lanzaboote Installation
Enroll the Secure Boot Keys
After the XML changes, boot the VM and enter the firmware setup (press ESC during boot).
-
Navigate to Device Manager → Secure Boot Configuration
-
Switch to “Custom mode”, uncheck Attempt Secure Boot [ ], select “Reset Secure Boot Keys”, save with F10, and reboot.
-
Enroll the keys:
sudo sbctl enroll-keys -m -
Reboot: After enrolling the keys and rebooting, the system will automatically be placed in “Standard mode”, and Attempt Secure Boot [x] selected. You do not need to re-enter firmware setup mode.
-
Verify Secure Boot Status:
bootctl status