This guide explains the mvirt build system and development workflow.
mvirt/
├── mvirt-cli/ # CLI client + TUI
├── mvirt-vmm/ # Daemon (VM Manager)
├── mvirt-log/ # Centralized audit logging
├── mvirt-zfs/ # ZFS storage management
├── mvirt-net/ # Virtual networking
├── mvirt-one/ # µOS - Minimal Linux for MicroVMs
│ ├── pideisn/ # Rust init process (PID 1)
│ ├── initramfs/ # rootfs skeleton
│ ├── kernel.config # Kernel config fragment
│ └── mvirt-one.mk # OS build rules
├── docs/ # Documentation
└── Makefile # Main build orchestration
Each service has its own proto/ subdirectory with gRPC definitions.
The build system uses GNU Make with a dependency-based approach. Targets only rebuild when their dependencies change.
| Target | Description |
|---|---|
make |
Build everything (Rust + UKI) |
make release |
Build Rust binaries (musl, static) |
make one |
Build mvirt-one (UKI) |
make kernel |
Build kernel only |
make initramfs |
Build initramfs only |
make clean |
Remove build artifacts |
make distclean |
Remove everything including kernel source |
make check |
Verify build dependencies are installed |
make docker |
Build in Docker (no local deps needed) |
The build system automatically resolves dependencies:
make one
└── $(UKI) # Unified Kernel Image
├── $(BZIMAGE) # Linux kernel
│ └── .config
│ └── kernel.config (fragment)
├── $(INITRAMFS) # Root filesystem
│ ├── pideisn # Init process
│ ├── mvirt # CLI
│ ├── mvirt-vmm # Daemon
│ ├── cloud-hypervisor
│ └── hypervisor-fw
└── cmdline.txt # Kernel command line
Running make one automatically builds all dependencies in the correct order.
Rust binaries are cross-compiled for musl to produce fully static executables:
cargo build --release --target x86_64-unknown-linux-muslThe binaries are:
pideisn- Init process (PID 1) for the mini-Linuxmvirt- CLI clientmvirt-vmm- VM manager daemonmvirt-log- Audit logging servicemvirt-zfs- ZFS storage daemonmvirt-net- Networking daemon
Build outputs:
| File | Description |
|---|---|
mvirt-one/target/mvirt-one.efi |
UKI (kernel + initramfs + cmdline) |
mvirt-one/target/cloud-hypervisor |
Downloaded hypervisor binary |
mvirt-one/target/hypervisor-fw |
Downloaded firmware |
- Edit code in any module (
mvirt-cli/,mvirt-vmm/,mvirt-zfs/,mvirt-net/,mvirt-log/,mvirt-one/pideisn/) - Run
cargo fmt && cargo clippy --workspaceto check formatting and lints - Run
make oneto rebuild UKI, orcargo buildfor daemons only
# Build UKI
make one
# Test with cloud-hypervisor (direct kernel boot)
cloud-hypervisor \
--kernel mvirt-one/target/mvirt-one.efi \
--cpus boot=1 --memory size=512M \
--console off --serial ttyBuild dependencies can be checked with:
make checkRequired packages (Debian/Ubuntu):
apt install build-essential flex bison libelf-dev libssl-dev bc
apt install systemd-ukify systemd-boot-efi # For UKI building
rustup target add x86_64-unknown-linux-musl # Rust musl targetBuild without installing dependencies locally:
make dockerThis builds a Docker image with all dependencies (Rust, musl, kernel build tools, etc.) and runs make iso inside the container. Files are owned by your user, not root.
The Docker image is cached, so subsequent builds are fast.
- Debian Trixie
- Rust + musl target
- Kernel build tools (flex, bison, libelf, etc.)
- UKI tools (systemd-ukify)
- ISO tools (isolinux, xorriso)
- protobuf compiler
# Build image only
docker build -t mvirt-builder .
# Run any make target
docker run --rm --user $(id -u):$(id -g) -v $(pwd):/work mvirt-builder make osAlways run before committing:
cargo fmt && cargo clippyNo warnings allowed in CI.