Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(microvm): add support for ext3, ext4, and xfs rootfs formats
Add configurable rootfs_format parameter to MicroVM class, allowing users
to specify the filesystem format (ext3, ext4, or xfs) when creating
rootfs images. The ext4 format remains the default to maintain backward
compatibility.

Changes include:
- Added rootfs_format configuration option with ext3, ext4, xfs support
- Updated _build_rootfs to format filesystems based on specified type
- Modified overlayfs file extension to match rootfs format
- Added validation for supported filesystem formats
- Included comprehensive test coverage for all formats
- Added example demonstrating filesystem format usage
  • Loading branch information
restuhaqza committed Feb 1, 2026
commit 059c2c935627d05481694040a978ebfc322960de
95 changes: 95 additions & 0 deletions examples/create_vm_with_filesystem_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
Example: Creating a MicroVM with Different Filesystem Formats

This example demonstrates how to create microVMs with different rootfs filesystem formats.
The firecracker-python library now supports ext3, ext4, and xfs filesystem formats.
"""

from firecracker import MicroVM

# Example 1: Create a VM with ext4 filesystem (default)
print("Creating VM with ext4 filesystem...")
vm_ext4 = MicroVM(
name="vm-ext4",
image="alpine:latest",
base_rootfs="./rootfs_ext4.img",
rootfs_size="5G",
rootfs_format="ext4", # Explicitly specify ext4 format
kernel_file="/var/lib/firecracker/kernel/vmlinux",
vcpu=2,
memory="2G",
verbose=True,
)

# Build the rootfs with ext4 filesystem
result = vm_ext4.build()
print(f"Result: {result}")

# Example 2: Create a VM with ext3 filesystem
print("\nCreating VM with ext3 filesystem...")
vm_ext3 = MicroVM(
name="vm-ext3",
image="alpine:latest",
base_rootfs="./rootfs_ext3.img",
rootfs_size="5G",
rootfs_format="ext3", # Use ext3 format
kernel_file="/var/lib/firecracker/kernel/vmlinux",
vcpu=2,
memory="2G",
verbose=True,
)

# Build the rootfs with ext3 filesystem
result = vm_ext3.build()
print(f"Result: {result}")

# Example 3: Create a VM with xfs filesystem
print("\nCreating VM with xfs filesystem...")
vm_xfs = MicroVM(
name="vm-xfs",
image="alpine:latest",
base_rootfs="./rootfs_xfs.img",
rootfs_size="5G",
rootfs_format="xfs", # Use xfs format
kernel_file="/var/lib/firecracker/kernel/vmlinux",
vcpu=2,
memory="2G",
verbose=True,
)

# Build the rootfs with xfs filesystem
result = vm_xfs.build()
print(f"Result: {result}")

# Example 4: Create and start a VM with a specific filesystem format
print("\nCreating and starting VM with xfs filesystem...")
vm = MicroVM(
name="running-vm-xfs",
image="ubuntu:24.04",
base_rootfs="./ubuntu_rootfs_xfs.img",
rootfs_size="10G",
rootfs_format="xfs",
kernel_file="/var/lib/firecracker/kernel/vmlinux",
ip_addr="172.16.0.10",
vcpu=2,
memory="4G",
expose_ports=True,
host_port=10222,
dest_port=22,
verbose=True,
)

# Create and start the VM
result = vm.create()
print(f"Result: {result}")

# Clean up (optional)
# vm.delete()

print("\n✓ All examples completed successfully!")
print("\nNotes:")
print("- Supported formats: ext3, ext4, xfs")
print("- Default format: ext4")
print("- The filesystem format is validated during initialization")
print("- Overlayfs also respects the specified format")
6 changes: 3 additions & 3 deletions firecracker/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
commit_id: COMMIT_ID
__commit_id__: COMMIT_ID

__version__ = version = '0.0.post136+g34afee786.d20260128'
__version_tuple__ = version_tuple = (0, 0, 'post136', 'g34afee786.d20260128')
__version__ = version = '1.0.3.post0'
__version_tuple__ = version_tuple = (1, 0, 3, 'post0')

__commit_id__ = commit_id = 'g34afee786'
__commit_id__ = commit_id = 'g79b65ab4e'
5 changes: 3 additions & 2 deletions firecracker/config.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import os
from dataclasses import dataclass


@dataclass
class MicroVMConfig:
"""Configuration defaults for Firecracker microVMs."""

data_path: str = "/var/lib/firecracker"
binary_path: str = "/usr/local/bin/firecracker"
snapshot_path: str = "/var/lib/firecracker/snapshots"
kernel_file: str = None
rootfs_size: str = "5G"
rootfs_format: str = "ext4"
initrd_file: str = None
init_file: str = "/sbin/init"
base_rootfs: str = None
Expand All @@ -27,4 +28,4 @@ class MicroVMConfig:
host_port: int = None
dest_port: int = None
vsock_enabled: bool = False
vsock_guest_cid: int = 3
vsock_guest_cid: int = 3
75 changes: 63 additions & 12 deletions firecracker/microvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class MicroVM:
image (str, optional): Docker image to use for the MicroVM
base_rootfs (str, optional): Path to the base rootfs file
rootfs_size (str, optional): Size of the rootfs file
rootfs_format (str, optional): Filesystem format for rootfs (ext3, ext4, xfs). Defaults to ext4
overlayfs (bool, optional): Whether to use overlayfs
overlayfs_file (str, optional): Path to the overlayfs file
vcpu (int, optional): Number of vCPUs
Expand Down Expand Up @@ -77,6 +78,7 @@ def __init__(
image: str = None,
base_rootfs: str = None,
rootfs_size: str = None,
rootfs_format: str = None,
overlayfs: bool = False,
overlayfs_file: str = None,
vcpu: int = None,
Expand Down Expand Up @@ -189,10 +191,20 @@ def __init__(
self._rootfs_file = os.path.join(self._rootfs_dir, base_rootfs_name)

self._rootfs_size = rootfs_size or self._config.rootfs_size

# Validate and set rootfs format
supported_formats = ["ext3", "ext4", "xfs"]
self._rootfs_format = rootfs_format or self._config.rootfs_format
if self._rootfs_format not in supported_formats:
raise ValueError(
f"Unsupported rootfs format '{self._rootfs_format}'. "
f"Supported formats are: {', '.join(supported_formats)}"
)

self._overlayfs = overlayfs or self._config.overlayfs
if self._overlayfs:
self._overlayfs_file = overlayfs_file or os.path.join(
self._rootfs_dir, "overlayfs.ext4"
self._rootfs_dir, f"overlayfs.{self._rootfs_format}"
)
self._overlayfs_name = os.path.basename(
self._overlayfs_file.replace("./", "")
Expand Down Expand Up @@ -311,7 +323,12 @@ def build(self):
if not self._docker_image:
return "No Docker image specified for building rootfs"

self._build_rootfs(self._docker_image, self._base_rootfs, self._rootfs_size)
self._build_rootfs(
self._docker_image,
self._base_rootfs,
self._rootfs_size,
self._rootfs_format,
)

return f"Rootfs built at {self._base_rootfs}"

Expand Down Expand Up @@ -370,7 +387,10 @@ def create(
f"Building rootfs from Docker image: {self._docker_image}"
)
self._build_rootfs(
self._docker_image, self._base_rootfs, self._rootfs_size
self._docker_image,
self._base_rootfs,
self._rootfs_size,
self._rootfs_format,
)

self._network.setup(
Expand Down Expand Up @@ -1545,37 +1565,68 @@ def _export_docker_image(self, image: str) -> str:
except Exception as e:
raise VMMError(f"Unexpected error: {e}")

def _build_rootfs(self, image: str, file: str, size: str):
def _build_rootfs(self, image: str, file: str, size: str, fs_format: str = "ext4"):
"""Create a filesystem image from a tar file.

Args:
image (str): Docker image name
file (str): Path to the output image file
size (str): Size of the image file
fs_format (str): Filesystem format (ext3, ext4, or xfs). Defaults to ext4

Returns:
str: Path to the created image file
"""
tmp_dir = None
try:
# Validate filesystem format
supported_formats = ["ext3", "ext4", "xfs"]
if fs_format not in supported_formats:
raise ValueError(
f"Unsupported filesystem format '{fs_format}'. "
f"Supported formats are: {', '.join(supported_formats)}"
)

self._download_docker(image)
tar_file = self._export_docker_image(image)

if not tar_file or not os.path.exists(tar_file):
return f"Failed to export Docker image {image}"

# Allocate file
run(f"fallocate -l {size} {file}")
if self._config.verbose:
self._logger.debug(f"Image file created: {file}")

run(f"mkfs.ext4 {file}")
if self._config.verbose:
self._logger.debug(f"Formatting filesystem: {file} with size {size}")

run(f"e2fsck -f -y {file}")
if self._config.verbose:
self._logger.debug(f"Filesystem check completed for: {file}")
# Format filesystem based on the specified format
if fs_format == "xfs":
run(f"mkfs.xfs -f {file}")
if self._config.verbose:
self._logger.debug(
f"Formatting filesystem: {file} with xfs (size {size})"
)
elif fs_format == "ext3":
run(f"mkfs.ext3 -F {file}")
if self._config.verbose:
self._logger.debug(
f"Formatting filesystem: {file} with ext3 (size {size})"
)
# Check filesystem for ext3
run(f"e2fsck -f -y {file}")
if self._config.verbose:
self._logger.debug(f"Filesystem check completed for: {file}")
else: # ext4 (default)
run(f"mkfs.ext4 -F {file}")
if self._config.verbose:
self._logger.debug(
f"Formatting filesystem: {file} with ext4 (size {size})"
)
# Check filesystem for ext4
run(f"e2fsck -f -y {file}")
if self._config.verbose:
self._logger.debug(f"Filesystem check completed for: {file}")

# Mount and extract
tmp_dir = tempfile.mkdtemp()
run(f"mount -o loop {file} {tmp_dir}")

Expand All @@ -1594,7 +1645,7 @@ def _build_rootfs(self, image: str, file: str, size: str):
)

if self._config.verbose:
self._logger.info("Build rootfs completed")
self._logger.info(f"Build rootfs completed with {fs_format} filesystem")

except Exception as e:
if tmp_dir:
Expand Down
Loading