Quickstart - Apple VZ (vfkit)
title: Quickstart — Apple VZ (vfkit)
Quickstart — Apple VZ via vfkit
The Path C / menu-then-reboot flow on Apple Silicon. We’ll use
vfkit — Apple’s officially
supported front-end for Virtualization.framework — to boot an
immutable boot.iso against a writable menu-cache.raw cache
disk, then reboot into Debian Trixie.
Why this and not Path A
VZ traps kexec_file_load(2) → Path A is out
VZ firmware has no HTTP/TCP/DHCP/DNS → Path B can't fetch plans
(the loader's networked phases
D–J were abandoned for VZ)
Path C: networking + FS walks happen in a Linux kernel
target loaded by firmware on next pass via BootOrder → ✓See Architecture / hypervisors for the protocol matrix and a longer discussion.
1. Install vfkit + dependencies
brew install vfkit qemu xorriso mtools dosfstools(QEMU is used as a build-time helper for the host-side tools; the runtime is vfkit.)
2. Build the immutable boot.iso
cd cloud-boot/uki
go run . build \
--arch aarch64 \
--iso boot.iso \
--cmdline "cloudboot.exit=reboot cloudboot.plan=oci://ghcr.io/me/cloud-plan:prod"The output is a hybrid GPT/El-Torito ISO. The El-Torito boot catalogue points at the UKI on the appended GPT FAT partition, so the same file boots equally from CD-ROM emulation, USB, or as a virtio-blk read-only disk.
3. Build the writable cache disk
cd cloud-boot/uki/scripts
./make-cache-disk.sh ../menu-cache.raw 256This creates a 256 MiB GPT disk with one FAT32 partition labelled
cloud-boot-cache. The label is what cloud-boot-init looks for
when staging targets. The script is idempotent — re-running it
won’t clobber existing Boot#### entries.
4. Boot
cd cloud-boot
vfkit \
--bootloader efi,variable-store=nvram.fd \
--device virtio-blk,path=uki/boot.iso,readonly \
--device virtio-blk,path=uki/menu-cache.raw \
--device virtio-blk,path=trixie.raw \
--device virtio-net,nat,mac=auto \
--device virtio-serial,logFilePath=vm.logWhat happens:
- Firmware loads
\EFI\BOOT\BOOTAA64.EFIfromboot.iso(UKI). - UKI runs
cloud-boot-initas PID 1. - virtio-net comes up, init fetches the plan over OCI.
- The plan resolves to a target with
disksource pointing at/dev/vda3(or whichever virtio-blk index Trixie landed at). cloud-boot-initreads/boot/vmlinuz-*and/boot/initrd.img-*via the ext4 userland driver, writes them to\EFI\Linux\<target>-vmlinuz.efi+<target>-initrdon the cache ESP.- Writes
Boot0001(LoadOption pointing at<target>-vmlinuz.efiwithinitrd=<target>-initrd <cmdline>as load options) andBootOrder = [0001, ...existing]viaefivarfs. reboot(LINUX_REBOOT_CMD_RESTART).- vfkit restarts. UEFI Boot Manager honours
BootOrder, runs<target>-vmlinuz.efifrom the cache ESP, distro kernel takes over viaExitBootServices.
You’ll see the bootstrap kernel boot once, then the Trixie kernel
boots immediately on the second pass — no second cloud-boot-init
run.
5. --gui framebuffer console
vfkit can show the guest’s framebuffer in a Cocoa window if the
guest kernel ships the right virtio-gpu drivers. The cloud kernel
variant already has them; see
kernel/cloud-arm64.config:
CONFIG_DRM_VIRTIO_GPU=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_VIRTIO_INPUT=yvfkit --gui \
--bootloader efi,variable-store=nvram.fd \
--device virtio-blk,path=uki/boot.iso,readonly \
--device virtio-blk,path=uki/menu-cache.raw \
--device virtio-blk,path=trixie.raw \
--device virtio-net,nat,mac=autoA vfkit window opens showing the boot console. Useful for
keyboard-driven menus — cloudboot.exit=reboot without an
explicit cloudboot.target= puts the user at an interactive menu.
6. Reset to the menu
Once a target has been staged, the firmware will keep booting it. To go back to the menu (e.g. to pick a different target), clear the NVRAM:
cd /sys/firmware/efi/efivars
sudo efibootmgr -b 0001 -B
sudo efibootmgr --bootorder $(efibootmgr | sed -n 's/^BootOrder: //p' | sed 's/0001,*//; s/,*0001//' )
sudo rebootOr use the convenience script uki/scripts/reset-cloud-boot.sh.
Troubleshooting
| Symptom | Likely cause |
|---|---|
vfkit: error: bootloader: file not found: nvram.fd | First run — vfkit creates nvram.fd automatically; re-run. |
Bootstrap kernel hangs at [init] virtio-net: timeout | Plan URL unreachable. Confirm --device virtio-net,nat,mac=auto is present, that the host has internet, and that the registry doesn’t require auth that you haven’t supplied. |
| Second-pass kernel doesn’t appear, vfkit keeps re-booting the UKI | The Boot0001 write failed. Check cloud-boot-init logs in vm.log. Common cause: menu-cache.raw doesn’t have the cloud-boot-cache partition label. Re-run make-cache-disk.sh. |
findESPDevice: no partition labelled cloud-boot-cache | Same as above. |
Next
- OpenStack with Keystone AC for production plans.
- Reference / NVRAM reset for the full ceremony.
- Filesystem drivers / RAID discovery for what happens when the chained distro is on btrfs RAID or ZFS raidz.