Modern Linux testing and continuous integration workflows demand rapid, isolated execution environments. While container engines like Docker or systemd-nspawn provide lightweight namespace isolation, they share the host kernel, preventing tests that require custom kernel modules, hypervisor hardware abstraction, or distinct storage architectures. Setting up full-scale virtualization suites like libvirt, KVM virt-manager, or cloud-init can be cumbersome for quick testing sessions. In recent systemd versions, the systemd-vmspawn tool bridges this gap by acting as a native virtual machine manager that boots raw OS disk images, raw partitions, and directory trees inside QEMU virtual machines using standard systemd invocation semantics.
Understanding systemd-vmspawn and QEMU Virtualisation
The systemd-vmspawn command provides a direct virtual machine counterpart to systemd-nspawn:
- True Hardware Virtualisation: Instead of creating a Linux namespace container,
systemd-vmspawnorchestrates QEMU/KVM under the hood to spin up an authentic hardware-isolated virtual machine with its own virtualised kernel, memory, and devices. - Direct Image Booting: Boots raw GPT disk images, raw squashfs images, and Unified Kernel Images (UKIs) conforming to the Discoverable Partitions Specification (DPS).
- Shared Journal Logging: Forwards the guest virtual machine console and journal logs directly to the host terminal or journal daemon.
- Container-Like Ergonomics: Starts, stops, and coordinates virtual machines with single-line commands without requiring XML configuration files or background hypervisor daemons.
Installing Prerequisites on Ubuntu
Because systemd-vmspawn delegates low-level virtualisation tasks to QEMU and KVM hardware accelerators, ensure the necessary virtualization packages are installed on Ubuntu:
sudo apt update
sudo apt install qemu-system-x86 qemu-utils ovmf systemd-container
Verify that hardware virtualisation extensions (Intel VT-x or AMD-V) are enabled on your system CPU:
egrep -c '(vmx|svm)' /proc/cpuinfo
A return value greater than 0 confirms hardware acceleration support. Ensure your current user belongs to the kvm group:
sudo usermod -aG kvm $USER
Booting a Disk Image Using systemd-vmspawn
To launch a virtual machine from an existing raw or QCOW2 operating system disk image (such as an Ubuntu Cloud image or custom appliance), use systemd-vmspawn -i:
sudo systemd-vmspawn -i ubuntu-noble-server-cloudimg.img
The tool automatically allocates virtual CPU cores, attaches the disk image as a virtual block device, invokes QEMU with KVM acceleration, and attaches your active terminal to the guest serial console.
To specify explicit CPU and memory allocations, use the --cpus and --ram switches. For example, to assign 4 virtual CPU cores and 4 GB of RAM:
sudo systemd-vmspawn --cpus=4 --ram=4096 -i ubuntu-noble-server-cloudimg.img
Booting a Directory Tree as a Virtual Machine
Similar to systemd-nspawn -D, systemd-vmspawn can boot directly into a root filesystem directory tree on the host without packaging it into a virtual disk first. It creates a temporary virtual block device or exports the filesystem via 9p / virtio-fs:
sudo systemd-vmspawn -D /srv/test-rootfs/
This allows developers to inspect, modify, and test root filesystem images on the host disk while booting them with complete virtual kernel isolation.
Configuring Virtual Machine Networking
By default, systemd-vmspawn establishes a user-mode virtual network (SLIRP) or virtual tap interface for the guest:
- Default User Mode: Provides outbound network access (DNS, HTTP/HTTPS) without requiring bridge setup on the host.
- Port Forwarding: To expose guest network ports (such as SSH on port 22 or a web server on port 8080) to the host system, specify the
--portargument:sudo systemd-vmspawn --port=2222:22 -i ubuntu-noble-server-cloudimg.img - You can then connect directly to the guest VM from your workstation terminal:
ssh -p 2222 user@localhost
Managing and Monitoring Active Virtual Machines
Virtual machines launched with systemd-vmspawn register with the systemd machine manager, allowing you to interrogate them using machinectl:
machinectl list
The output displays the active VM, its operational class (vm), hypervisor backend (qemu), and internal OS version.
To inspect resource consumption, memory allocation, and CPU load for the running virtual machine:
machinectl status ubuntu-noble-server-cloudimg
To gracefully shut down or terminate the guest virtual machine from another terminal:
sudo machinectl poweroff ubuntu-noble-server-cloudimg
To force-terminate an unresponsive guest:
sudo machinectl terminate ubuntu-noble-server-cloudimg