MicroVM.build() turns a Docker image into a rootfs image file that Firecracker can boot. Call it before create().
from firecracker import MicroVM
vm = MicroVM(
image="my-rootfs:latest",
base_rootfs="/var/lib/firecracker/rootfs.img",
rootfs_size="10G",
rootfs_format="ext4",
kernel_file="./firecracker-files/vmlinux",
)
print(vm.build()) # Rootfs built at /var/lib/firecracker/rootfs.img
vm.create()create() does not build the image for you. It checks that base_rootfs exists and fails if it does not.
- Run as root.
build()usesfallocate,mkfsandmount -o loop. - Docker must be running.
- For
rootfs_format="xfs", installmkfs.xfs(packagexfsprogs). - Free disk space in
/var/lib/firecrackerfor a temporary tar file of the whole image, plusrootfs_sizeat thebase_rootfspath.
| Argument | Required | Default | Description |
|---|---|---|---|
image |
Yes | Docker image name, for example my-rootfs:latest. |
|
base_rootfs |
Yes | Path of the image file to create. | |
rootfs_size |
No | "5G" |
Size of the image file. Any size that fallocate -l accepts. |
rootfs_format |
No | "ext4" |
"ext3", "ext4" or "xfs". |
kernel_file is not used by build(), but create() needs it.
- Pulls the image if it is not local. This step runs in the constructor, not in
build(). The constructor raisesValueErrorif the image does not exist locally or in the registry. - Creates a container from the image and exports its file system to
/var/lib/firecracker/rootfs_<image>.tar. - Removes the container.
- Creates the image file with
fallocate, formats it, and checks it withe2fsck(ext3 and ext4 only). - Mounts the image file, extracts the tar file into it, and unmounts it.
- Deletes the tar file.
build() returns "Rootfs built at <base_rootfs>". It raises VMMError if a step fails.
Warning:
build()does not check the exit code offallocate,mkfsandmount. If you run it without root, it can report success with an empty or broken image. Run it as root, and check the image if the VM does not boot.
build() copies the files of the image and nothing more. A plain ubuntu:24.04 or alpine:latest image builds without errors, but the VM does not boot to a login.
Your image must contain:
- An init system. The kernel starts
/sbin/init. Installsystemd-sysv(Debian, Ubuntu) or an equivalent. - An SSH server, if you want to use
connect(). - Your SSH public key in
/root/.ssh/authorized_keys.
This Dockerfile gives a VM that you can log in to as root:
FROM ubuntu:24.04
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
systemd systemd-sysv init openssh-server \
iproute2 iputils-ping curl ca-certificates && \
rm -rf /var/lib/apt/lists/*
COPY id_ed25519.pub /root/.ssh/authorized_keys
RUN chmod 700 /root/.ssh && chmod 600 /root/.ssh/authorized_keysCreate the key and build the image:
ssh-keygen -t ed25519 -f id_ed25519 -N ""
docker build -t my-rootfs:latest .Then build the rootfs with the Python code at the top of this page, and connect:
vm.connect(key_path="./id_ed25519")Docker manages /etc/resolv.conf in containers, so the exported file is usually empty. The guest then has network access but cannot resolve names.
Add a name server to the image after build():
TMP=$(mktemp -d)
sudo mount -o loop /var/lib/firecracker/rootfs.img "$TMP"
echo "nameserver 1.1.1.1" | sudo tee "$TMP/etc/resolv.conf"
sudo umount "$TMP"
rmdir "$TMP"| Format | Notes |
|---|---|
ext4 |
Default. Works with the Firecracker CI kernels. |
ext3 |
Supported. |
xfs |
Needs mkfs.xfs on the host and XFS support in the guest kernel. |
examples/create_vm_with_filesystem_format.py builds one image of each format.
create() copies base_rootfs into the VM directory. Every VM starts from a clean copy, and the image you built stays unchanged. Build the image once and create many VMs from it.
| Problem | Cause |
|---|---|
ValueError: base_rootfs is required when image is provided |
Pass base_rootfs with image. |
ValueError: Invalid Docker image: ... |
The image does not exist locally or in the registry. |
create() fails with Base rootfs not found |
Call build() before create(). |
| The VM boots but stops early | The image has no init system. |
connect() fails |
The image has no SSH server or no authorized_keys, or the guest is still booting. |
| The guest cannot resolve names | /etc/resolv.conf is empty. See Add DNS. |