Windows Server 2022 VM from published image fails UEFI boot (BdsDxe: Not Found, no filesystem visible in Boot From File)
Environment
- Incus version: 7.2 (Zabbly packages)
- Host OS: Debian 13
- Topology: 4-node cluster (Lenovo ThinkAgile HX630)
- Storage backend: LVM (
lvmclusterwith sanlock/lvmlockd) on shared QNAP iSCSI LUNs, multipath viamultipathd - Guest: Windows Server 2022, UEFI (default
security.securebootsettings, no CSM) - Projects: instance launched in a non-default project (
Staging), image published from a VM in the same cluster
What I did
- Installed Windows Server 2022 in an Incus VM (UEFI). Installation and boot worked fine on the original VM.
- Published the VM as an image:
incus stop <source-vm> incus publish <source-vm> --alias win2022-template - Launched a new instance from the published image:
incus launch win2022-template trim-vm-stg-t1-s21-h2h-permata --vm ...
Problem
The new instance fails to boot. EDK2 output:
BdsDxe: failed to load Boot0002 "UEFI QEMU QEMU HARDDISK " from PciRoot(0x0)/Pci(0x1,0x1)/Pci(0x0,0x0)/Scsi(0x0,0x1): Not Found
>>Start PXE over IPv4.
The disk itself is detected at SCSI level (it appears in the Boot0002 device path), but the firmware cannot find a bootable EFI application on it and falls through to PXE.
What I’ve tried / observed
-
Expected NVRAM behavior: I understand that UEFI NVRAM variables are per-instance and not carried in a published image, so the original “Windows Boot Manager” entry pointing to
\EFI\Microsoft\Boot\bootmgfw.efiis gone on the new instance, and the firmware falls back to the removable-media path\EFI\BOOT\BOOTX64.EFI, which Windows does not install on fixed disks. My plan was to boot manually once via the firmware menu and then install the fallback loader. -
Firmware menu → Boot Maintenance Manager → Boot From File: here it gets worse — the File Explorer is completely empty. No volumes are listed at all, so I cannot even manually browse to
bootmgfw.efi. This suggests the firmware sees no readable FAT filesystem (i.e. no ESP) on the disk, not just a missing boot entry. -
Inspecting the volume from the host: the instance’s block volume exists as expected:
# lvs | grep -i permata virtual-machines_Staging_trim--vm--stg--t1--s21--h2h--permata vg-qnap-hdd-01 -wi------k 500.00m virtual-machines_Staging_trim--vm--stg--t1--s21--h2h--permata.block vg-qnap-hdd-01 -wi------k 66.01gPartition table of the
.blockLV (afterlvchange -aey -K):<PASTE: fdisk -l /dev/vg-qnap-hdd-01/virtual-machines_Staging_trim--vm--stg--t1--s21--h2h--permata.block>Contents of the first partition (via
kpartx), if mountable:<PASTE: ls -laR of mounted ESP, or mount error> -
Instance config:
<PASTE: incus config show trim-vm-stg-t1-s21-h2h-permata --expanded> -
Source VM: the original VM the image was published from booted normally before publishing. (If still available: same
fdisk -loutput of the source VM’s.blockLV for comparison.)<PASTE if available>
Questions
- Is
incus publishexpected to preserve the full GPT layout including the EFI System Partition for VM images, or are there known cases (e.g. with LVM block volumes / clustered LVM) where the image ends up without a valid partition table or ESP? - Is there a supported way to have Windows instances from a published image boot without manual firmware intervention — i.e. does Incus regenerate any default boot entries, or is installing a fallback
\EFI\BOOT\BOOTX64.EFIinside the template the recommended approach? - Could
volume.size/ rounding on the target pool truncate the image on unpack (original disk size vs. 66.01g on the new instance), cutting off the backup GPT or data? - Any recommended way to debug what
incus publishactually captured (e.g. inspecting the unified tarball /rootfs.imgof the published image)?
Config Yaml VM
incus config show vm_name --expanded --project Staging
architecture: x86_64
config:
raw.qemu: -boot menu=on,splash-time=10000
volatile.base_image: 2783afba8794a00a7ebe3b0ef7c4122dce6579767c2654f651cdc438e5cb2e6f
volatile.cloud-init.instance-id: 34e7c79f-a2bf-480d-84cb-a75cd42ba81d
volatile.eth809.hwaddr: 10:66:6a:d2:44:33
volatile.last_state.power: STOPPED
volatile.last_state.ready: "false"
volatile.uuid: ad838e63-0b29-4977-85e8-71537a6f50c8
volatile.uuid.generation: ad838e63-0b29-4977-85e8-71537a6f50c8
volatile.vm.rtc_adjustment: "-2"
volatile.vm.rtc_offset: "0"
volatile.vsock_id: "822980815"
devices:
eth809:
nictype: bridged
parent: br-vm
type: nic
vlan: "xxx"
root:
path: /
pool: pool-qnap-hdd-01
size: 66GiB
type: disk
ephemeral: false
profiles:
- os-trim-win2022
- net-vlanxxxx-stg-thirdparty
stateful: false
description: ""
Happy to provide any additional logs (incus info --show-log, qemu.log, image metadata). Thanks!