Creating a VM Image
MicroVM cannot boot from a block device via BIOS/UEFI because of the virtio-mmio limitations. We need to create a root filesystem image that is booted directly using a Linux kernel.
Preparation
We need:
1. Linux kernel (vmlinuz) - From the host or a custom build
2. Root filesystem image - An ext4 filesystem inside a file
3. Init system (optional) - BusyBox or systemd
Creating a Root Filesystem with Debootstrap
Debootstrap creates a minimal Debian/Ubuntu rootfs without a full installation:
# Install debootstrap
sudo apt install debootstrap
# Create a 2GB file image
fallocate -l 2G rootfs.img
# Format as ext4
mkfs.ext4 -L microvm-root rootfs.img
# Mount the image
sudo mkdir -p /mnt/microvm
sudo mount -o loop rootfs.img /mnt/microvm
# Install minimal Debian
sudo debootstrap --variant=minbase bookworm /mnt/microvm http://deb.debian.org/debian
# Cleanup
sudo umount /mnt/microvm
Creating an Image with Alpine (Lighter)
Alpine Linux is very well suited to MicroVM because of its small size:
# Download the Alpine mini rootfs
wget https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/x86_64/alpine-minirootfs-3.19.1-x86_64.tar.gz
# Create the image
fallocate -l 512M rootfs.img
mkfs.ext4 -L alpine-root rootfs.img
# Mount and extract
sudo mkdir -p /mnt/microvm
sudo mount -o loop rootfs.img /mnt/microvm
sudo tar -xzf alpine-minirootfs-3.19.1-x86_64.tar.gz -C /mnt/microvm
# Basic configuration
sudo chroot /mnt/microvm /bin/sh -c '
echo "microvm" > /etc/hostname
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100" >> /etc/inittab
'
# Setup fstab
echo "/dev/vda / ext4 defaults 0 1" | sudo tee /mnt/microvm/etc/fstab
# Cleanup
sudo umount /mnt/microvm
Creating an Image from the Host Kernel
Use the host kernel to avoid a manual compile:
# Find the available kernels
ls /boot/vmlinuz*
# Copy the kernel to the working directory
cp /boot/vmlinuz-$(uname -r) vmlinuz
# Copy the initramfs (optional, if needed)
cp /boot/initrd.img-$(uname -r) initrd.img
Building a Custom MicroVM Kernel (Optional)
For optimal performance, build a kernel with a minimal configuration:
# Download kernel source
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.tar.xz
tar xf linux-6.7.tar.xz
cd linux-6.7
# Start from defconfig
make defconfig
# Enable only what is needed
scripts/config --enable CONFIG_VIRTIO
scripts/config --enable CONFIG_VIRTIO_MMIO
scripts/config --enable CONFIG_VIRTIO_BLK
scripts/config --enable CONFIG_VIRTIO_NET
scripts/config --enable CONFIG_EXT4_FS
scripts/config --enable CONFIG_SERIAL_8250
scripts/config --enable CONFIG_SERIAL_8250_CONSOLE
scripts/config --disable CONFIG_PCI
scripts/config --disable CONFIG_ACPI
scripts/config --disable CONFIG_USB
scripts/config --disable CONFIG_SOUND
scripts/config --disable CONFIG_DRM
scripts/config --disable CONFIG_WIRELESS
scripts/config --disable CONFIG_BLUETOOTH
scripts/config --disable CONFIG_SECURITY
# Build
make -j$(nproc)
# Copy the result
cp arch/x86/boot/bzImage ../vmlinuz-microvm
Configuring the Network Inside the Image
# Mount the image
sudo mount -o loop rootfs.img /mnt/microvm
# Configure the network interface
sudo tee /mnt/microvm/etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
EOF
# Enable SSH (optional)
sudo chroot /mnt/microvm apk add openssh # Alpine
# or
sudo chroot /mnt/microvm apt install -y openssh-server # Debian
# Set up the root password
sudo chroot /mnt/microvm passwd -d root
sudo umount /mnt/microvm
Quick Start: Image Minimal
The fastest way to create an image for MicroVM:
#!/bin/bash
# Script: create-microvm-image.sh
set -e
IMAGE_SIZE="1G"
IMAGE_NAME="microvm-rootfs.img"
ALPINE_URL="https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/x86_64/alpine-minirootfs-3.19.1-x86_64.tar.gz"
# Download the Alpine rootfs
wget -q --show-progress "$ALPINE_URL" -O alpine-rootfs.tar.gz
# Create the image
fallocate -l "$IMAGE_SIZE" "$IMAGE_NAME"
mkfs.ext4 -q -L microvm "$IMAGE_NAME"
# Mount and populate
MNT=$(mktemp -d)
sudo mount -o loop "$IMAGE_NAME" "$MNT"
sudo tar -xzf alpine-rootfs.tar.gz -C "$MNT"
# Basic config
echo "microvm" | sudo tee "$MNT/etc/hostname" > /dev/null
echo "nameserver 8.8.8.8" | sudo tee "$MNT/etc/resolv.conf" > /dev/null
echo "/dev/vda / ext4 defaults,noatime 0 1" | sudo tee "$MNT/etc/fstab" > /dev/null
# Enable auto login on ttyS0
sudo mkdir -p "$MNT/etc/init.d"
echo '#!/sbin/openrc-run' | sudo tee "$MNT/etc/init.d/autoconsole" > /dev/null
echo 'depend() { keyword timeout 0; }' | sudo tee -a "$MNT/etc/init.d/autoconsole" > /dev/null
echo 'start() { /sbin/getty -L ttyS0 115200 vt100 & }' | sudo tee -a "$MNT/etc/init.d/autoconsole" > /dev/null
sudo chmod +x "$MNT/etc/init.d/autoconsole"
sudo chroot "$MNT" rc-update add autoconsole default 2>/dev/null || true
sudo umount "$MNT"
rmdir "$MNT"
echo "Image created: $IMAGE_NAME"
echo "Kernel needed: vmlinuz (from host or custom build)"
Verifying the Image
# Check the filesystem
file microvm-rootfs.img
# Output: microvm-rootfs.img: Linux rev 1.0 ext4 filesystem data...
# Check mounting
sudo mount -o loop microvm-rootfs.img /mnt/microvm
ls /mnt/microvm/
sudo umount /mnt/microvm