This is the first moment your VM comes alive: direct boot with --kernel vmlinux and PVH, then firmware boot with rust-hypervisor-firmware and CLOUDHV.fd (edk2) for full OSes like Ubuntu. This episode dissects both paths, prepares the kernel and cloud image, and verifies that the VM console successfully displays kernel logs.

With the binary installed in episode 3, the most anticipated moment has arrived: bringing a VM to life for the first time. In episode 4 we take the two boot paths that are the foundation of the entire series — direct boot (the kernel is loaded directly by the VMM) and firmware boot (UEFI/EDK2 or hypervisor-fw boots the system).
Why understand both? Because they serve different needs. Direct boot gives full control and the fastest boot — ideal for microVMs and Kata Containers. Firmware boot is required to run a complete operating system with its own boot loader, such as Ubuntu with GRUB or Windows. You'll use both depending on the workload.
For direct boot, you need a Linux kernel in vmlinux (ELF) or bzImage form. Cloud Hypervisor uses the PVH boot protocol to load the ELF directly — a protocol designed specifically so a hypervisor can boot a kernel without firmware.
A practical way to get a kernel: download one from a distribution or from the Cloud Hypervisor releases page, which provides specially built kernels:
wget https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/v53.0/kernel-vmlinux
file kernel-vmlinuxfile should identify it as an ELF executable. The Cloud Hypervisor-built kernel already includes the virtio-blk and virtio-net drivers and the needed configs — perfect for this series' experiments.
Provide a rootfs image. We'll start with the Ubuntu cloud image and convert it to raw format with qemu-img (we discuss storage formats in depth in episode 6):
qemu-img convert -O raw ubuntu-24.04-server-cloudimg-amd64.img ubuntu.raw
qemu-img info ubuntu.rawqemu-img convert -O raw copies the image contents into raw format so it can be read directly by virtio-blk. Later, in episode 9, we'll set up cloud-init so the guest can be logged into.
The most basic structure for running Linux via direct boot:
cloud-hypervisor \
--kernel kernel-vmlinux \
--disk path=ubuntu.raw \
--cpus boot=4 \
--memory size=4G \
--net tap=ch0,ip=192.168.100.1,mac=a8:21:95:80:35:e6 \
--console off \
--serial tty \
--cmdline "console=ttyS0 root=/dev/vda1 rw quiet"Let's break down the flags:
--kernel kernel-vmlinux: the kernel is loaded directly via PVH — no firmware.--disk path=ubuntu.raw: the first virtio-blk disk (seen by the guest as /dev/vda).--cpus boot=4: four vCPUs.--memory size=4G: 4 GB of RAM.--net tap=ch0,ip=192.168.100.1,mac=...: a TAP NIC with the host IP on the TAP bridge.--console off --serial tty: redirects the serial to the terminal so kernel logs are visible.--cmdline: kernel parameters — root=/dev/vda1 points to the root partition on the raw disk.If the boot succeeds, you'll see kernel log lines followed by the Ubuntu login prompt. root=/dev/vda1 must match the partition layout of the image you're using.
Important
The root= parameter in --cmdline is the most common cause of boot failure: if the root partition isn't at /dev/vda1 (e.g., the image uses LVM or a differently numbered partition), the kernel will panic with VFS: Unable to mount root fs. Identify the partition first with qemu-nbd or guestfish before guessing.
rust-hypervisor-firmware is a minimal Rust-based firmware that performs basic hardware setup then jumps to the kernel. Here's how to use it:
cloud-hypervisor \
--firmware hypervisor-fw \
--kernel kernel-vmlinux \
--disk path=ubuntu.raw \
--cpus boot=4 \
--memory size=4G \
--serial ttyNote that the kernel is still provided via --kernel; the firmware only bridges the initial setup. This path mimics a real firmware boot flow, so it's closer to actual hardware behavior.
For full operating systems — Ubuntu with GRUB, Fedora, Debian, or Windows — use UEFI firmware based on edk2, provided as CLOUDHV.fd:
cloud-hypervisor \
--firmware CLOUDHV.fd \
--disk path=ubuntu.raw \
--cpus boot=4 \
--memory size=4G \
--net tap=ch0,ip=192.168.100.1,mac=a8:21:95:80:35:e6 \
--serial ttyHere there's no --kernel or --cmdline: the UEFI firmware reads the disk, runs the GRUB stored in the image, and GRUB loads the kernel. This is the same flow as booting your laptop — just inside a VM.
A practical comparison:
direct boot (PVH) < hypervisor-fw < CLOUDHV.fd (UEFI/GRUB)
tercepat paling lengkap--serial tty and console=ttyS0 in the cmdline are consistent./dev/vda1 vs /dev/vda15 for UEFI images).CLOUDHV.fd.Tip
Save boot commands in a script or a JSON config file (episode 5) from the start. Once the list of options grows — disk, fs, net, hotplug — retyping everything each time is typo-prone. Reproducibility is part of production.
Key takeaways:
--kernel vmlinux uses the PVH protocol: fast and firmware-free.--cmdline passes kernel parameters; root= must match the image's root partition.hypervisor-fw is a minimal firmware for initial setup before the kernel.CLOUDHV.fd (edk2) is needed for full OSes (GRUB) and Windows.--serial tty, console=ttyS0) is your eyes during boot.In the next episode, episode 5, we'll configure CPU, memory, and machine config — --cpus boot=N with topology and features, --memory with hotplug and hugepages, and a comparison of CLI flags vs. a JSON config file via --config. This is the foundation for designing VMs that truly match workload needs.