mvmctl docs

mvmctl documentation

Everything you need to install, configure, and manage microVMs with mvmctl. Covers every command with explanations, callouts, and real-world examples.

Before you begin

mvmctl runs on Linux only — Firecracker requires KVM, which is not available on macOS or Windows. Make sure your system meets these requirements before installing.

  • Linux host (x86_64 or aarch64) with KVM support — check with ls /dev/kvm
  • Access to /dev/kvm and membership in the kvm group
  • Go 1.26.3+ to build from source (optional — binary install does not require Go)
  • Root access once for host setup (mvm init handles this for you)
  • nftables for NAT and firewall rules (default backend)

System packages

mvmctl depends on a few system tools for networking, image handling, and cloud-init:

Terminal

sudo apt-get install -y iproute2 iptables nftables qemu-utils e2fsprogs util-linux procps kmod openssh-client tar sudo passwd fakeroot
On Ubuntu 24.04+, install the prebuilt binary for the fastest setup. Use mvm --help to verify installation.

Install mvm

Two ways to install. The prebuilt binary is the fastest — no Go toolchain needed.

No Go toolchain required. Best for production machines.

Terminal

# Get the latest binary from the Releases page
# https://github.com/AlanD20/mvmctl/releases
mkdir -p ~/.local/bin
curl -L -o ~/.local/bin/mvm https://github.com/AlanD20/mvmctl/releases/latest/download/mvm
chmod +x ~/.local/bin/mvm
mvm --help
After installing, run mvm --help to verify. If "command not found", ensure ~/.local/bin is in your PATH.

Initialize host

Before creating any VMs, your host needs one-time setup: KVM module loading, IP forwarding, the mvm group, sudoers permissions, and bridge networking. This is what mvm init handles for you.

Interactive setup (recommended)

Run mvm init — it walks you through host config (sudo/group/sudoers), Firecracker binary download, cache initialization, service binary extraction, and default asset setup. Escalates to root automatically when needed.

Terminal

mvm init
When prompted to log out/in, do it. Group membership changes only apply to new login sessions. If you skip this, subsequent commands will fail with permission errors. Alternatively run newgrp mvm to avoid logging out.

One-time host setup

Run mvm init to perform the one-time machine setup. It is idempotent — safe to re-run.

Terminal

mvm init
mvm init escalates to root when needed. It creates the mvm system group, writes sudoers drop-in files, loads KVM kernel modules, enables IP forwarding, and sets up bridge/TAP networking. Normal mvm commands do not need sudo after this runs.

What host init actually does

  • Loads kvm, kvm_intel/kvm_amd kernel modules (networking modules tun and bridge are loaded as needed)
  • Enables net.ipv4.ip_forward for NAT networking
  • Creates the mvm system group and adds your user to it
  • Writes a sudoers drop-in to /etc/sudoers.d/mvm so mvmctl can run privileged commands (ip, iptables, sysctl, modprobe) without password prompts
  • Sets up the mvm-net bridge and firewall chains for NAT

mvm init flags

Control the interactive wizard behavior with these flags:

Terminal

# Non-interactive mode — use defaults, skip all prompts
mvm init --non-interactive

# Skip host init step (useful if host is already configured)
mvm init --skip-host

# Skip default network creation
mvm init --skip-network

# Combine flags for fully automated setup
mvm init --non-interactive --skip-host
Combine --skip-host and --skip-network to skip both host setup and default network creation. Useful when re-running mvm init to only pull new assets.

Other host commands

Terminal

mvm host status   # Show current host configuration state vs expected — useful for verifying setup
mvm host status --json   # Show current host configuration state as JSON
mvm host info     # Show host hardware, limits, and VM capacity projection
mvm host info --refresh  # Re-detect hardware and limits before displaying
mvm host info --json     # Output as JSON
mvm host clean    # Remove networking config only (bridges, TAPs, iptables rules)
mvm host reset    # Full rollback — networking, sysctl, sudoers, and the mvm group
mvm host reset is destructive. It removes the mvm group, meaning anyone in it loses mvm access. Only use if you are permanently uninstalling mvmctl.

Create your first VM

This walkthrough takes you from zero to a running microVM: generate an SSH key, download a kernel and OS image, boot the VM, connect, and clean up.

Step by step

Create a complete microVM from scratch: generate an SSH key, download a kernel and OS image, boot the VM, connect, and clean up.

Terminal

# 1. Generate an SSH key for VM access
mvm key create my-key --default

# 2. Download Firecracker-optimized kernel (~30s)
mvm kernel pull --type firecracker

# 3. Download an Ubuntu 24.04 image
mvm image pull ubuntu --version 24.04

# 4. Create and start the VM
mvm vm create myvm --image ubuntu:24.04

# 5. Wait for cloud-init to finish (~30-60s)
mvm logs myvm --follow

# 6. SSH into the VM
mvm ssh myvm

# 7. List running VMs
mvm vm ls

# 8. Remove the VM when done
mvm vm rm myvm -f
Cloud-init takes 30-60 seconds on first boot. The VM is running during this time — it is just running startup scripts. Watch progress with mvm logs myvm --follow.
You need at least one SSH key set up before creating a VM. Use mvm key create NAME --default or pass --ssh-key explicitly when creating a VM.
Images are several hundred MB. Ensure at least 2 GB free in ~/.cache/mvmctl/.

mvm vm create

Creates and immediately starts a Firecracker microVM. Under the hood mvmctl: (1) copies the root filesystem image to a per-VM directory, (2) generates a Firecracker JSON boot config, (3) picks an available IP from the network lease pool, (4) starts a temporary HTTP server for cloud-init, (5) launches Firecracker (+ jailer). There is no separate "start" command — the VM boots right away.

All flags

Terminal

VM_NAME (positional)          VM name (required). Used to identify the VM in all commands.
--image IMAGE              Image name, type:version (e.g. ubuntu:24.04), short ID, or path to .ext4 file. Auto-detected from defaults if omitted.
--kernel KERNEL            Kernel short ID or path to vmlinux. Auto-detected from defaults if omitted.
--vcpu N                   Number of vCPUs (default: from user config).
--mem, --memory N          Memory in MiB or GiB (e.g. 512M, 1G) (default: from user config).
--disk-size, -s SIZE       Rootfs disk size (e.g. 1G, 512M or 1024M). Default: from config.
--ssh-key KEY              SSH public key name (from key cache) or file path.
--user USER                Default SSH user for cloud-init. Default: from config.
--ip ADDRESS               Static guest IP, e.g. 172.27.0.42. Default: auto-assigned.
--network, --net NAME      Named network to attach to. Default: 'net'.
--mac ADDRESS              Custom MAC address. Auto-generated if omitted.
--cloud-init-mode MODE     One of: off (default), inject, iso, net.
--cloudinit-config PATH    Path to custom cloud-init configuration file.
--nocloud-net-port PORT    Port for nocloud-net HTTP server (0=auto-assign).
--no-pci                   Disable PCI transport (default: enabled). Required for hotplug support.
--nested-virt              Enable nested virtualization (requires PCI, adds kvm-intel/amd.nested=1 boot arg)
--cpu-template PATH        Path to CPU template JSON file (merged with nested-virt config if both set)
--console                  Enable serial console relay (default: disabled).
--lsm-flags FLAGS          Linux Security Module flags for kernel cmdline.
--enable-logging           Enable Firecracker logging.
--enable-metrics           Enable Firecracker metrics.
--boot-args ARGS           Custom kernel boot arguments (e.g. 'console=ttyS0 reboot=k panic=1').
--writeback                Use writeback cache mode for drives.
--vsock-port PORT          Vsock port for the guest agent (default: 1024).
--allow-remote-exec        Allow inter-VM remote execution (VM→Host→VM relay). Disabled by default.
--skip-cleanup             Skip cleanup on failure (for debugging).
--skip-deblob              Skip debloat operations on rootfs (removes OS caches, package manager caches).
--count, -c N              Number of VMs to create (default: 1).
--atomic                   If any VM fails, remove all successfully-created VMs (all-or-nothing).
--volume, -v NAME          Attach a volume to the VM (can be specified multiple times).
--force, -f                Skip confirmation prompts.

Examples

Minimal — get a default VM running fast

Terminal

# Assuming kernel + image fetched, default key set:
mvm vm create myvm --image ubuntu:24.04

Defaults: 1 vCPU, 512 MiB RAM, auto-assigned IP on network net (172.27.0.0/24).

Custom resources

Terminal

mvm vm create \
  build-vm \
  --image ubuntu:24.04 \
  --vcpus 4 \
  --mem 8192 \
  --disk-size 50G

Useful for CI runners or compiling in isolated environments.

Specific network and static IP

Terminal

# First create a network
mvm network create isolated --subnet 10.0.0.0/24

# Then attach the VM with a fixed IP
mvm vm create myvm --image ubuntu:24.04 --network isolated --ip 10.0.0.50

The IP must fall within the network subnet. Default network net uses 172.27.0.0/24.

Non-root user and custom SSH key

Terminal

mvm key create workstation-key --default
mvm vm create \
  dev-vm \
  --image ubuntu:24.04 \
  --ssh-key workstation-key \
  --user ubuntu

The --user flag sets the cloud-init default user. This user gets password-less sudo inside the VM.

Alpine — lightweight and fast

Terminal

mvm image pull alpine --version 3.21
mvm vm create tiny-vm --image alpine:3.21 --vcpus 1 --mem 256

Alpine boots in seconds. Great for testing or ephemeral workloads.

Custom cloud-init user-data

Terminal

# Write a custom user-data file
cat > my-user-data.yaml << 'EOF'
#cloud-config
package_update: true
packages:
  - htop
  - build-essential
EOF

# Pass it to the VM
mvm vm create myvm --image ubuntu:24.04 --cloudinit-config my-user-data.yaml

Custom user-data merges with mvmctl's default cloud-init. You can add packages, write files, run commands, etc.

Batch creation with atomic rollback

Terminal

# Create 5 VMs atomically — if any fails, all are removed
mvm vm create cluster --image ubuntu:24.04 --count 5 --atomic

# With a volume attached to a single VM
mvm volume create shared-data 10G
mvm vm create worker --image ubuntu:24.04 --volume shared-data

The --count flag auto-generates names by appending -1, -2, etc. The --atomic flag ensures no partial creation — all created VMs are rolled back if any single VM fails. --count and --volume are mutually exclusive (a volume can only be attached to one VM). Implicit IP and MAC assignment is used (no --ip or --mac with --count > 1).

Firecracker is not a hypervisor you can "pause" like VirtualBox. mvm vm create starts the VM immediately.
Each VM gets an IP from the lease pool. The default network (net) uses 172.27.0.0/24. Leases are reused when VMs are removed.
The --disk-size flag resizes via qemu-img resize. It only grows the image — shrinking requires manual intervention.
If you do not set --ssh-key and have no default key, the VM will boot but you cannot SSH in. Use mvm console for serial access instead.

VM Lifecycle

Once your VM is created, these commands let you interact with, inspect, snapshot, and tear it down.

mvm ssh

SSH into a running VM by name. Resolves the VM name to its IP address from the lease database and connects with the cached SSH key.

Terminal

mvm ssh myvm

SSH as the default user (root by default unless --user was specified).

Terminal

mvm ssh myvm --user admin

SSH as a specific user.

Terminal

mvm ssh myvm --cmd "uname -a"

Execute a command non-interactively.

Terminal

mvm ssh myvm --key mykey

Use a specific SSH key.

Terminal

mvm ssh myvm --timeout 10

Set connection timeout in seconds.

SSH only works after cloud-init finishes. On first boot, cloud-init generates SSH host keys and configures the default user. This takes 30-60 seconds.
mvmctl automatically passes the correct SSH key and user. You do not need ssh -i commands. But you do need a key set up beforehand.
Works on custom networks too — mvmctl looks up the correct IP from the lease database.

mvm console

Attaches a PTY-based serial console to a VM using a vsock relay. No network stack required. Works even if the VM has no IP or cloud-init failed.

Terminal

mvm console myvm

Attach to the VM serial console interactively.

Terminal

mvm console myvm --state

Check if the console relay is running (does not attach).

Terminal

mvm console myvm --kill

Kill a stuck console relay.

Press Ctrl+X then D to detach from the console session. This does not shut down the VM.
The console relay runs as a background process. If it crashes, use mvm console --kill then re-attach.
Use --state to check if the relay is running without attaching. Handy for scripting.
Requires the vhost_vsock kernel module. Check with lsmod | grep vsock.

mvm exec

Execute a command inside a VM via the vsock guest agent. No SSH or network stack required — works over the vsock channel directly.

Terminal

mvm exec myvm -- uname -a

Run a command as root and see output.

Terminal

mvm exec myvm --user ubuntu -- whoami

Run a command as a specific user.

Terminal

mvm exec myvm --timeout 30 -- long-running-command

Override the default vsock probe timeout.

Terminal

mvm exec myvm --no-sync -- echo "fast"

Skip final sync for faster execution.

Terminal

mvm exec myvm --port 1025 -- my-command

Specify a custom vsock port.

Unlike mvm ssh --cmd, mvm exec does not need SSH keys or a network connection. It uses the vsock agent inside the VM.
Use --user to run as a specific user (default: root).
Use --timeout to set the vsock agent connect/probe timeout in seconds.
Use --no-sync to skip the final sync() (faster but risks data loss on VM stop).
The VM must have the vsock agent running. This is true for any VM created with mvmctl.

mvm logs

View or stream VM logs. Two types: boot (serial console — kernel boot messages, cloud-init, login prompts) and OS (Firecracker process stderr/stdout).

Terminal

mvm logs myvm --follow

Watch the VM boot in real-time. Best for checking if cloud-init finished.

Terminal

mvm logs myvm --os

Check Firecracker stderr — useful if the VM failed to start.

Terminal

mvm logs myvm

View the full boot log (static, not following).

--follow / -f streams logs in real-time (like tail -f). Press Ctrl+C to stop.
Use --os to show Firecracker process logs instead of serial console output.
Use --lines / -n to limit output to the last N lines.
Log files are in ~/.cache/mvmctl/vms/<vm-sha>/ as firecracker.console.log and firecracker.log.

mvm vm snapshot / load

Create and restore VM snapshots — save memory and disk state to disk, then restore later. Useful for preserving a long-running VM state before rebooting the host.

Terminal

mvm snapshot create myvm

Snapshot a running VM. Saves memory and disk state.

Terminal

mvm snapshot ls

List all snapshots.

Terminal

mvm snapshot inspect <snapshot-id>

Show detailed snapshot information.

Terminal

mvm snapshot restore <snapshot-id> <vm-name>

Restore the VM from a saved snapshot.

Terminal

mvm snapshot rm <snapshot-id>

Remove a snapshot.

Snapshots can be large — memory + disk. A VM with 2 GiB RAM creates a ~2 GiB memory file.
The VM continues running while being snapshotted. The snapshot is crash-consistent (like pulling the power cord).
Snapshots are stored in ~/.cache/mvmctl/snapshots/<id>/.

mvm cp

Copy files between the host and microVMs using a binary frame protocol over vsock. The vsock agent inside the VM handles file transfer operations.

Terminal

mvm cp local-file.txt myvm:/home/

Copy a file from host to VM.

Terminal

mvm cp myvm:/var/log/syslog ./

Copy a file from VM to host.

Terminal

mvm cp myvm1:/data/file.txt myvm2:/data/

Copy a file between two VMs.

Terminal

mvm cp *.txt myvm:/dst/

Shell glob expands to multiple files (host → VM only).

Uses a binary frame protocol over vsock — no SSH or tar dependency needed inside the VM. Transfers are encrypted by the host-guest isolation boundary.
Path format: use vm_name:/remote/path for VM paths, plain /local/path for local paths.
Multiple sources are allowed for host → VM copies.

mvm vm ps

List only running VMs (active Firecracker processes). Shows name, status, IP, resources, and image/kernel IDs.

Terminal

mvm vm ps

Show only VMs that are currently running or starting.

mvm vm ls / inspect

Shows detailed VM information: SHA256 hash ID, IP address, network, kernel path, image path, resources, creation time, current state, and vsock agent config (guest CID, UDS path, port, agent version, upgrade state).

Terminal

mvm vm inspect myvm

Show all details for a VM.

Terminal

mvm vm ls

List all VMs with brief info (name, IP, status).

Terminal

mvm vm ls --json

List all VMs with JSON output.

mvm vm rm / prune

Stops the Firecracker process, removes firewall rules, kills the nocloud-net server, and deletes the VM state directory.

Terminal

mvm vm rm myvm

Remove a VM with confirmation.

Terminal

mvm vm rm myvm -f

Remove without asking (script-friendly).

Terminal

mvm cache prune vm

Remove all stopped VMs at once.

Without --force / -f, the command asks for confirmation. Use --force / -f in scripts.
mvm cache prune vm removes all stopped VMs at once. Asks for confirmation by default.
Removing a VM frees its IP lease, making it available for new VMs.
Stopped VMs (crashed or killed) still show in mvm vm ls until removed with rm or prune.

Resource Management

mvmctl manages five resource types: OS images (root filesystems), kernels (vmlinux binaries), Firecracker/jailer binaries, SSH keys, and persistent data disks (volumes).

mvm image

What images are

Images are root filesystem images (ext4 format) that provide the OS for your microVM. mvmctl can fetch pre-built images from the registry or import local files.

Available images

These image types are defined in mvmctl and can be fetched with mvm image pull <type>:<version> (e.g. mvm image pull ubuntu:24.04) or the longer mvm image pull <type> --version <version> — or use mvm image ls --remote to see all available versions:

  • ubuntu — Ubuntu LTS (tar-rootfs). Versions: 26.04, 24.04, 22.04, 20.04
  • ubuntu-minimal — Ubuntu Minimal (tar-rootfs). Versions: 26.04, 24.04, 22.04, 20.04
  • debian — Debian (qcow2). Versions: 13, 12, 11
  • alpine — Alpine Linux (VHD). Versions: 3.x releases
  • archlinux — Arch Linux (qcow2, rolling release — no version needed)
  • firecracker — Firecracker CI Ubuntu (squashfs, from Firecracker S3 bucket)

Fetching images

Terminal

# Fetch an image by type and version
mvm image pull ubuntu --version 24.04

# Shorthand: type:version syntax
mvm image pull ubuntu:24.04

# Alpine Linux
mvm image pull alpine --version 3.21

# Or use shorthand:
mvm image pull alpine:3.21

# Arch Linux (rolling release — no version needed)
mvm image pull archlinux

# Force re-download (overwrites cached copy)
mvm image pull ubuntu:24.04 -f

# Skip cached version listing and fetch live
mvm image pull ubuntu:24.04 --no-cache

# Disable specific detectors (type,label,size,filesystem,all)
mvm image pull ubuntu:24.04 --disable-detector type,label

# Set as default after download
mvm image pull ubuntu:24.04 --default

# List available images (local + remote versions)
mvm image ls
mvm image ls --remote            # Show upstream versions
mvm image ls --remote --no-cache # Bypass cache, fetch live from upstream
Shorthand alias: mvm img <command> can be used instead of mvm image <command>. Images are typically 200-800 MB compressed. Cached in ~/.cache/mvmctl/images/. Each VM gets its own copy.

Importing custom images

Import a rootfs from a file or create a reusable base image from a running VM:

Terminal

# From a VM selector — create a reusable base image from a VM's rootfs
mvm image import base-img my-vm

# From a VM selector, with a version tag
mvm image import base-img:v1.0 my-vm --version v2.0

# From a file (qcow2, raw, tar-rootfs)
mvm image import my-custom-image /path/to/my-custom-image.raw --format raw

# Overwrite existing image
mvm image import myimg /path/to/image.raw --force

# Set as default after import
mvm image import myimg /path/to/image.raw --default

# Skip filesystem optimization (shrink/compression)
mvm image import myimg /path/to/image.raw --skip-optimization

# Disable specific detectors
mvm image import myimg /path/to/image.raw --disable-detector type

mvm image ls                     # Verify it shows up
mvm image default my-custom-image
Syntax: mvm image import NAME [SOURCE | VM_SELECTOR]. When the second argument is a VM selector (name or ID), mvmctl copies that VM's rootfs to create a reusable image. Supports raw images (.raw/.img), qcow2, and tar-rootfs archives (.tar/.tar.gz/.tar.xz/.tgz) from files.

Managing images

Terminal

mvm image ls                          # List all cached images
mvm image inspect <id>                # Show detailed image info
mvm image default <id>             # Set default for new VMs
mvm image rm <id>                     # Remove a cached image (full or short SHA)
mvm image warm <id>                   # Pre-decompress to ready pool for fast VM creation

mvm kernel

What kernels are

Firecracker requires an uncompressed ELF binary (vmlinux) — not the compressed vmlinuz used by traditional bootloaders. mvmctl supports two kernel types.

Firecracker-optimized kernel (recommended)

A pre-built kernel from the Firecracker CI pipeline. Minimally configured for fast boot — no PCI, no ACPI. Downloads in ~30 seconds.

Terminal

mvm kernel pull --type firecracker
# Downloads the latest Firecracker-optimized kernel
This is the default. Use this unless you need custom kernel modules or a specific version. Boots in under 200ms.

Official upstream kernel (custom build)

Downloads the official Linux kernel source (default: 6.19.9) and compiles it with a Firecracker-compatible config. Takes 10-30 minutes.

Terminal

# Build latest upstream kernel
mvm kernel pull --type official

# Using type:version shorthand
mvm kernel pull official:6.19.9

# Build a specific version
mvm kernel pull --type official --version 6.6

# Apply a custom kernel config fragment
mvm kernel pull --type official --config /path/to/my-fragment.config

# Enable kernel features (kvm, nftables)
mvm kernel pull official:6.19.9 --features kvm,nftables

# Specify parallel build jobs
mvm kernel pull --type official --jobs 8

# Set as default after fetch
mvm kernel pull --type official --default

# Force clean rebuild (bypass cache)
mvm kernel pull --type official --clean-build

# Keep the build directory for debugging
mvm kernel pull --type official --keep-build-dir
Official builds require build deps: build-essential, flex, bison, libelf-dev, libssl-dev, libncurses-dev, bc, git, curl, pkg-config, dwarves (for pahole). Expect 10-30 min build times. Use --config PATH to apply a custom kernel config fragment, --jobs N for parallel build jobs, and --default to set as default after fetch.

Managing kernels

Terminal

mvm kernel ls                    # List cached kernels
mvm kernel ls --remote           # List remote versions available for download
mvm kernel inspect <id>         # Show detailed kernel info
mvm kernel default <id>        # Set as default for VM creation
mvm kernel import <name> <path>  # Import a custom vmlinux kernel file
mvm kernel rm <id>               # Remove a cached kernel

Kernel feature reference

The --features flag applies pre-defined kernel config fragments on top of the base Firecracker config. Multiple features can be combined (e.g. --features kvm,nftables,tuntap). Use --features all or --features * to enable every feature in the spec. Enabled features are persisted and shown in mvm kernel inspect. Defined in kernels.yaml.

FeatureDescriptionKey config enforced
kvmNested KVM virtualizationCONFIG_KVM, CONFIG_KVM_INTEL / CONFIG_KVM_AMD
nftablesnftables NAT firewall backendCONFIG_NFT_NAT, CONFIG_NF_TABLES_INET, CONFIG_NFT_MASQ
tuntapTUN/TAP device support (VM networking)CONFIG_TUN
btrfsBtrfs filesystem supportCONFIG_BTRFS_FS, CONFIG_BTRFS_FS_POSIX_ACL
containersContainer runtime core (containerd/runc)CONFIG_NAMESPACES, CONFIG_CGROUPS, CONFIG_SECCOMP, CONFIG_OVERLAY_FS, CONFIG_IKCONFIG, CONFIG_IKCONFIG_PROC
iptablesiptables kube-proxy backendCONFIG_NETFILTER, CONFIG_NETFILTER_XTABLES, CONFIG_IP_NF_NAT, CONFIG_NF_CONNTRACK, CONFIG_IP_SET, CONFIG_NETFILTER_XT_SET, CONFIG_NETFILTER_XT_MARK, CONFIG_NETFILTER_XT_TARGET_CT
cni-bridgeCNI bridge / overlay networkingCONFIG_BRIDGE, CONFIG_VETH, CONFIG_MACVLAN, CONFIG_IPVLAN, CONFIG_VXLAN, CONFIG_GENEVE, CONFIG_FIB_RULES
ebpfeBPF / BTF (Cilium, bpftool)CONFIG_BPF, CONFIG_BPF_JIT, CONFIG_DEBUG_INFO_BTF, CONFIG_BPF_EVENTS, CONFIG_PERF_EVENTS, CONFIG_NET_CLS_BPF, CONFIG_NET_CLS_ACT, CONFIG_NET_SCH_INGRESS
storageFilesystems for persistent volumesCONFIG_EXT4_FS, CONFIG_XFS_FS, CONFIG_BTRFS_FS, CONFIG_BLK_DEV_NVME
fqdn-proxyL7 / FQDN policy proxy (TPROXY, xt_socket)CONFIG_NETFILTER_XT_TARGET_TPROXY, CONFIG_NETFILTER_XT_TARGET_CT, CONFIG_NETFILTER_XT_MATCH_SOCKET
bandwidthBandwidth manager (FQ packet scheduler)CONFIG_NET_SCH_FQ
iscsi-targetiSCSI target mode (Longhorn block storage)CONFIG_TARGET_CORE, CONFIG_ISCSI_TARGET, CONFIG_ISCSI_TCP, CONFIG_BLK_DEV_SD
ebpf-cnieBPF-based CNI networking (Cilium, Hubble)CONFIG_BPF, CONFIG_DEBUG_INFO_BTF, CONFIG_VXLAN, CONFIG_GENEVE, CONFIG_IP_SET, CONFIG_NETFILTER_XT_TARGET_TPROXY, CONFIG_NETFILTER_XT_MATCH_SOCKET
iscsi-targetiSCSI target mode (Longhorn block storage)CONFIG_TARGET_CORE, CONFIG_ISCSI_TARGET, CONFIG_ISCSI_TCP, CONFIG_BLK_DEV_SD
ebpf-cnieBPF-based CNI networking (Cilium, Hubble)CONFIG_BPF, CONFIG_DEBUG_INFO_BTF, CONFIG_VXLAN, CONFIG_GENEVE, CONFIG_IP_SET, CONFIG_NETFILTER_XT_TARGET_TPROXY, CONFIG_NETFILTER_XT_MATCH_SOCKET

mvm bin

What binaries are

Firecracker and jailer binaries downloaded from the Firecracker GitHub releases page. You need at least one version downloaded to create VMs.

Managing binaries

Terminal

# Download Firecracker v1.15.0 (includes jailer)
mvm bin pull firecracker --version 1.15.0

# Build Firecracker from source at a git ref
mvm bin pull firecracker --git-ref v1.15.0

# Force re-download even if version already exists
mvm bin pull firecracker --version 1.15.0 --force

# List downloaded versions
mvm bin ls

# JSON output for scripting
mvm bin ls --json

# List remote versions available for download
mvm bin ls --remote

# Set as active version by ID prefix
mvm bin default abc123

# Remove by version
mvm bin rm --version 1.15.0
mvm bin rm --version 1.15.0 -f   # Force remove even if referenced by VMs

# Remove by ID
mvm bin rm abc123
mvm bin pull downloads both firecracker and jailer together. They must match versions — mixing v1.14 firecracker with v1.15 jailer causes runtime errors.

mvm key

What keys are for

SSH public keys cached by mvmctl for injection into VMs via cloud-init. Without at least one key, you cannot SSH into your VMs (console access still works).

Creating and managing keys

Terminal

# Generate a new ED25519 keypair and set as default (recommended)
mvm key create mykey --default

# Import an existing public key
mvm key import mykey ~/.ssh/id_ed25519.pub
mvm key import mykey ~/.ssh/id_ed25519.pub --force   # Overwrite if key exists

# List all cached keys
mvm key ls

# Show key details
mvm key inspect mykey

# Set default keys for VM creation
mvm key default mykey

# Clear all default keys
mvm key default --clear

# Export a key to a directory
mvm key export mykey ~/.ssh/exported

# Remove a key from cache
mvm key rm mykey
mvm key create generates both a public and private key. The private key stays on your machine — mvmctl only stores the public key for cloud-init injection.

How keys work with VMs

When you create a VM with --ssh-key mykey (or use the default key), mvmctl injects the public key into cloud-init user-data. After cloud-init finishes (~30-60s), you can SSH in with mvm ssh myvm.

mvm volume

What volumes are

Volumes are persistent data disks that can be attached to VMs and survive VM removal. They are stored as raw or qcow2 disk images in the cache directory and managed through the mvmctl database.

Creating volumes

Create a new persistent volume with a name and size:

Terminal

# Create a 10 GB raw volume
mvm volume create data-disk 10G

# Create a qcow2 volume (supports grow and shrink)
mvm volume create data-disk 10G --format qcow2

# Create a read-only volume (writable by default)
mvm volume create data-disk 10G --read-only

# Create a 512 MB volume for testing
mvm volume create test-disk 512M
Shorthand alias: mvm vol <command> can be used instead of mvm volume <command>. Raw format (default) uses fallocate for fast allocation. Qcow2 format supports both grow and shrink via qemu-img. The volume name must be unique.

Listing volumes

Terminal

# List all volumes with ID, name, format, size, status, and VM
mvm volume ls

# JSON output for scripting
mvm volume ls --json

Inspecting volumes

Show detailed information about a volume including disk metadata:

Terminal

mvm volume inspect data-disk
mvm volume inspect abc123    # ID prefix also works

Removing volumes

Terminal

# Remove by name
mvm volume rm data-disk

# Remove multiple volumes
mvm volume rm data-disk test-disk

# Force remove even if attached to a VM
mvm volume rm data-disk -f
A volume can only be removed if it is not attached to a running VM, unless --force / -f is used. Detach first with mvm volume detach.

Resizing volumes

Grow (or shrink qcow2) an existing volume to a new size:

Terminal

# Resize to 20 GB (grow only for raw format)
mvm volume resize data-disk 20G

# Raw volumes: grow only (fallocate)
# Qcow2 volumes: grow and shrink (qemu-img)
Raw format supports grow only (via fallocate). Qcow2 format supports both grow and shrink (via qemu-img). Shrinking below the actual data size causes data loss.

Attaching volumes to VMs

Volumes can be attached and detached from running VMs using the VM command group:

Terminal

# Attach a volume when creating a VM
mvm vm create myvm --image ubuntu:24.04 --volume data-disk

# Attach to a running VM (via Firecracker API)
mvm volume attach myvm data-disk

# Detach from a running VM
mvm volume detach myvm data-disk
A volume can only be attached to one VM at a time. Attaching to a second VM automatically detaches from the first if supported by the Firecracker API.

Network Management

mvmctl uses Linux bridge/TAP networking with NAT (via nftables or iptables). Each named network is a separate bridge with its own subnet.

How networking works

mvmctl uses Linux bridge/TAP networking with NAT (via nftables or iptables). Each named network is a Linux bridge with its own subnet. VMs get TAP interfaces and IPs from a lease pool. Traffic is NATed to the host network.

The default network

The default network is called net and uses 172.27.0.0/24 (gateway: 172.27.0.1). It is created automatically the first time you run mvm host init — no manual network setup needed for basic use.

The default network name is net. The bridge device is named mvm-net (mvm-net), following the convention mvm-<network-name>.

Network commands

Terminal

# Create a named network with a custom subnet
# You will be prompted to select interface(s) for NAT
mvm network create mynet --subnet 10.0.1.0/24

# Create non-interactively (skips NAT gateway prompts)
mvm network create mynet --subnet 10.0.1.0/24 --non-interactive

# Create with explicit NAT gateway interfaces
mvm network create mynet --subnet 10.0.1.0/24 --nat-gateways eth0

# Create without NAT (no internet access for VMs)
mvm network create mynet --subnet 10.0.1.0/24 --no-nat

# Create with explicit gateway IP
mvm network create mynet --subnet 10.0.1.0/24 --ipv4-gateway 10.0.1.1

# Create and set as default network
mvm network create mynet --subnet 10.0.1.0/24 --default

# List all networks
mvm network ls

# Show network details
mvm network inspect mynet

# Set a network as default
mvm network default mynet

# Sync firewall rules between database and host
mvm network sync

# Remove a network (only if no VMs attached)
mvm network rm mynet
Shorthand alias: mvm net <command> can be used instead of mvm network <command>. You cannot remove a network that has VMs attached. Stop and remove the VMs first.

Using networks with VMs

Terminal

# Create a VM on a custom network
mvm network create isolated --subnet 10.0.0.0/24
mvm vm create myvm --image ubuntu:24.04 --network isolated

# Assign a specific IP
mvm vm create myvm --image ubuntu:24.04 --network isolated --ip 10.0.0.50

Custom networks get the subnet you specify. The bridge device is named mvm-<network-name>. Each VM gets a unique MAC address (auto-generated with the 02:FC prefix).

Configuration

Configuration priority

Settings resolve in this order (lower overrides higher):

  1. Built-in defaults from constants.go (compiled into the binary, lowest priority)
  2. SQLite database (~/.cache/mvmctl/mvmdb.db) — canonical store for user overrides
  3. MVM_* environment variables (e.g. MVM_LOG_LEVEL, MVM_CACHE_DIR)
  4. CLI flags (highest priority)

Config and cache location

All configuration is stored in the SQLite database at ~/.cache/mvmctl/mvmdb.db (override with MVM_CACHE_DIR). The config directory at ~/.config/mvmctl/ holds SSH keys (override with MVM_CONFIG_DIR).

Config commands

Terminal

# List all overridable settings and their current values
mvm config list

# Get a specific value
mvm config get defaults.vm vcpu_count

# Set a value (persists to database)
mvm config set defaults.vm vcpu_count 4

# Reset a single value to default
mvm config reset defaults.vm vcpu_count

# Reset all overrides globally
mvm config reset --all

Environment variables

Terminal

MVM_CACHE_DIR          Override cache directory               ~/.cache/mvmctl
MVM_CONFIG_DIR         Override config directory               ~/.config/mvmctl
MVM_LOG_LEVEL          Log level: DEBUG, INFO, WARN, ERROR     WARN (CLI flags recommended)
MVM_WARM_POOL          Warm image pool backend (disk or tmpfs)   tmpfs (default)
MVM_TEMP_DIR           Override temp directory for microVMs       /tmp/mvmctl
MVM_ESCALATED          Set by sudo wrapper to indicate             1
                       privilege escalation
MVM_ASSET_MIRROR       Local directory for asset mirroring
MVM_SUDO_RESTART       Set internally when re-running                     (not set)
                       <code>mvm init</code> with sudo
The --verbose (sets INFO) and --debug (sets DEBUG) CLI flags are the recommended way to control log level — they take precedence over MVM_LOG_LEVEL. The env var falls back to WARN if neither flag is set and the variable is unset.

Cache management

Terminal

# Initialize cache directories
mvm cache init

# Prune specific resource type
mvm cache prune vm
mvm cache prune network
mvm cache prune image
mvm cache prune kernel
mvm cache prune binary
mvm cache prune misc

# Dry-run prune all (see what would be removed)
mvm cache prune --all --dry-run

# Prune all resources including protected items
mvm cache prune --all

# Prune all without confirmation
mvm cache prune --all --force

# Completely clean all cache (nuclear option)
mvm cache clean
mvm cache clean --dry-run
Always run --dry-run first. Cache pruning is one-way. mvm cache clean removes ALL cached assets AND host networking, but does not touch running VMs unless you use --all.

Shell Completion

Shell completion

Generate tab-completion scripts for your shell. Uses Cobra's built-in generators — completions adapt automatically as commands change. Supports bash, zsh, fish, and PowerShell.

Terminal

# bash — add to ~/.bashrc
source <(mvm completion bash)

# zsh — add to ~/.zshrc (inline)
source <(mvm completion zsh)

# zsh — or place in fpath for compinit auto-load
mvm completion zsh > "${fpath[1]}/_mvm"

# fish
mvm completion fish > ~/.config/fish/completions/mvm.fish
Run mvm completion --help for the full installation guide per shell.

Self-Update

mvm self-update

Check for and apply binary updates from GitHub Releases. Downloads the latest release matching your architecture and verifies the SHA256 checksum before swapping.

Terminal

mvm self-update              # Check + apply if newer
mvm self-update check        # Check only, print available version
mvm self-update apply        # Force apply (even if same version)

Dependencies

mvmctl depends on several system binaries. Most are common Linux utilities; this reference covers what each is for and which package provides it.

Core runtime dependencies

These binaries are required for basic mvmctl operations:

BinaryPurposeDebian/UbuntuRHEL/FedoraArch
firecracker + jailerMicroVM VMM + security isolationmvm bin pullmvm bin pullmvm bin pull
ipBridge/TAP managementiproute2iproute2iproute2
iptablesNAT and firewall rulesiptablesiptablesiptables
iptables-savePersisting iptables rulesiptablesiptablesiptables
nft / nftablesNAT and firewall rules (default backend)nftablesnftablesnftables
sysctlIP forwardingprocpsprocps-ngprocps-ng
modprobeKVM module loadingkmodkmodkmod
lsmodKVM module statuskmodkmodkmod
groupaddmvm group creationpasswdshadowshadow
usermodUser group membershippasswdshadowshadow
visudoSudoers validationsudosudosudo
sudoPrivileged commandssudosudosudo
groupdelRemoving the mvm group on resetpasswdshadowshadow
dumpe2fsFilesystem inspectione2fsprogse2fsprogse2fsprogs

Image & cloud-init dependencies

BinaryPurposeDebian/UbuntuRHEL/FedoraArch
qemu-imgImage conversion/resizeqemu-utilsqemu-imgqemu-img
sfdiskPartition table manipulationutil-linuxutil-linuxutil-linux
blkidRoot partition/UUID detectionutil-linuxutil-linuxutil-linux
mount/umountImage mountingutil-linuxutil-linuxutil-linux
truncateSparse file creationcoreutilscoreutilscoreutils
mkfs.ext4Rootfs formattinge2fsprogse2fsprogse2fsprogs
fakerootPreserve tarball ownership during extractionfakerootfakerootfakeroot
unsquashfsSquashFS extractionsquashfs-toolssquashfs-toolssquashfs-tools
tarTarball extractiontartartar
cloud-localdsCloud-init seed ISOcloud-image-utilscloud-utilscloud-utils
ssh-keygenSSH key generationopenssh-clientopensshopenssh
sshVM connectionopenssh-clientopensshopenssh

libguestfs (optional — for cloud-init direct injection)

Required only if you enable the GuestFS backend via mvm config set settings guestfs_enabled true. The loop-mount provisioner is the default and does not require libguestfs.

Terminal

# Debian/Ubuntu
sudo apt-get install libguestfs0 libguestfs-tools supermin

# RHEL/CentOS/Fedora
sudo dnf install libguestfs libguestfs-tools supermin

# Arch
sudo pacman -S libguestfs supermin

Building kernels from source

Building official kernels from source requires additional build dependencies (make, gcc, flex, bison, libelf, openssl, ncurses, bc, pahole, git, curl, pkg-config). See the full custom-kernel guide for details.

Provisioning backend

mvmctl handles provisioning internally through a configurable backend. See the project documentation for backend details.

Command dependencies

Each mvmctl command may depend on system tools that are verified during mvm init. See DEPENDENCIES.md for details.

Host system requirements

  • Kernel modules: kvm, kvm_intel or kvm_amd, tun, bridge, vhost_vsock, nft_chain_nat
  • Hardware virtualization: VT-x (Intel) or AMD-V must be enabled in BIOS/UEFI
  • Permissions: The user must be in the mvm group (created by mvm host init)

Cloud-Init

Cloud-init configures your VM on first boot: users, SSH keys, networking, startup scripts. mvmctl handles this automatically.

What is cloud-init?

Cloud-init configures your VM on first boot: sets up users, injects SSH keys, configures networking, runs startup scripts. mvmctl handles this automatically.

How mvmctl handles cloud-init

Defaults to off if not specified. Set via --cloud-init-mode on mvm vm create. The available modes are:

Cloud-init modes

  • inject — injects cloud-init files directly into the rootfs via the active provisioner backend (loop-mount by default, or guestfs if enabled). Fastest and most reliable.
  • net — starts a temporary HTTP server (nocloud-net). The VM fetches config during boot via ds=nocloud;seedfrom=http://GATEWAY_IP:PORT/. No libguestfs needed.
  • iso — attaches a CD-ROM ISO with cloud-init files. Compatible with all images. Slower (requires cloud-localds).
  • off (default) — disables cloud-init entirely. VM boots with no user setup.

How nocloud-net (net mode) works

  • A temporary HTTP server starts on an available port (8000-9000 range)
  • Firewall rules allow only the specific VM to reach its server
  • The VM boots with the nocloud-net kernel command line datasource
  • Cloud-init fetches meta-data, user-data, and network-config via HTTP
  • The HTTP server auto-cleans up when the VM is removed

Security model

  • Each VM gets its own HTTP server on a unique port
  • Source-based firewall rules — only the VM's IP can reach its server
  • Servers bind to the bridge gateway IP, not 0.0.0.0
  • Rules are tagged with # nocloudnet:<vm_name>:<port>

Troubleshooting

Common issues, what causes them, and how to fix them:

Permission denied: /dev/kvm

Terminal

# First check if /dev/kvm exists:
ls -l /dev/kvm

# Case 1 — does not exist (KVM modules not loaded):
sudo modprobe kvm
sudo modprobe kvm_intel    # or kvm_amd on AMD

# Case 2 — exists but not writable (group membership):
sudo usermod -aG kvm $USER
# Then log out and back in

If /dev/kvm does not exist after modprobe, install KVM modules (e.g. linux-modules-extra-* on Ubuntu). Group membership takes effect on next login.

Bridge mvm-net not found

Terminal

# The bridge is created automatically. Ensure host init ran:
mvm init

Re-running mvm init is safe (idempotent). The default bridge is named mvm-net.

Image not found

Terminal

mvm image pull ubuntu --version 24.04
mvm image ls   # Verify it appears

Image IDs are case-sensitive. Use mvm image ls to see available images.

Kernel not found

Terminal

mvm kernel pull --type firecracker
mvm kernel ls           # Verify it is cached
mvm kernel ls --remote  # List available remote versions

Default fetch downloads a Firecracker-optimized kernel (~30s). Official builds take 10-30 min.

Firecracker binary not found

Terminal

mvm bin pull firecracker --version 1.15.0
mvm bin default <id>

Always run mvm bin default <id> after fetching. The default version (e.g. 1.15.0) matches the installed Firecracker release — you can also pull other versions with mvm bin pull firecracker --version <version>.

VM won't boot / SSH times out

Terminal

# Watch boot progress:
mvm logs myvm --follow

# If nothing at all, check Firecracker process log:
mvm logs myvm --os

Wait at least 60 seconds before assuming the VM is stuck. If the boot log shows nothing, the kernel may be incompatible or the image corrupt.

NoCloud-net server failed to start

Terminal

# Port range (8000-9000) may be exhausted
sudo ss -tlnp | grep -E ':(8[0-9]{3}|9[0-9]{3})'
# Kill orphaned servers
pkill -f "mvm run nocloudnet serve"

Each VM uses one port in 8000-9000. If many VMs were not cleaned up, orphaned servers may still be running.

Mixed firewall backends (Docker conflict)

Terminal

# Symptom: VM has IP, ping works, but TCP times out
# Detection:
mvm config get settings firewall_backend
sudo nft list ruleset | grep -c 'MVM-'

# Fix 1: sync firewall rules from database
mvm network sync

# Fix 2: reboot host (clears all firewall state cleanly)
sudo reboot

# Fix 3: Configure Docker to use the same backend as mvmctl
# Then re-run: mvm host init

Docker and mvmctl may use different firewall backends (nftables vs iptables-legacy). Rules go to different places. mvm network sync reloads rules from the database.

Network creation fails with permission denied

Terminal

# Check mvm group membership
groups | grep mvm

# If not in group:
sudo usermod -aG mvm $USER
# Then log out and back in

The mvm group also requires a new login session. Use newgrp mvm to avoid logging out.

Console relay not working

Terminal

# Check relay status
mvm console myvm --state

# Kill and re-attach
mvm console myvm --kill
mvm console myvm

The console relay uses vsock. Ensure vhost_vsock kernel module is loaded: lsmod | grep vsock.

Cache corruption or stale state

Terminal

# Preview what would be removed
mvm cache prune --all --dry-run

# Remove stale entries from a specific type
mvm cache prune vm

# Full reset (removes ALL VMs — careful!)
mvm cache prune --all

Cache corruption usually shows as metadata pointing to deleted files, or phantom VMs. mvm cache prune reconciles metadata with actual files.

Volume attach fails — volume is already attached

Terminal

# Check volume status
mvm volume inspect my-data

# Detach from current VM first
mvm volume detach <vm-name> my-data

# Force remove (use with caution)
mvm volume rm my-data -f

A volume can only be attached to one VM at a time. Detach it first or create a new volume. Use --force / -f on volume rm to skip the attached check.

Debug mode

Enable verbose logging to see what mvmctl is doing under the hood:

Terminal

# Run a single command with verbose (INFO) output:
mvm --verbose vm create myvm --image ubuntu:24.04

# Run a single command with debug (DEBUG) output:
mvm --debug vm create myvm --image ubuntu:24.04

# Or use the MVM_LOG_LEVEL env var (takes precedence over defaults,
# but CLI flags --verbose/--debug take precedence over the env var):
MVM_LOG_LEVEL=DEBUG mvm vm create myvm --image ubuntu:24.04
The --verbose flag sets INFO level; --debug sets DEBUG level. CLI flags take precedence over MVM_LOG_LEVEL. Falls back to WARN if neither flag nor env var is set.

Getting help

Still stuck? Open an issue on GitHub with:

  • The exact command you ran
  • Full error output (run with MVM_LOG_LEVEL=DEBUG)
  • Your OS and mvm --version