uvm

uvm is a small, local Firecracker microVM command-line manager for Ubuntu. It creates one Firecracker process per VM, attaches it to a host TAP device, and persists enough state to list, stop, and destroy the VM later.

This directory is a self-contained project. All UVM code, tests, packaging metadata, launcher scripts, and documentation live here.

For architecture, lifecycle invariants, extension guidance, and contributor workflows, see DEVELOPER.md.

For end-to-end operator scenarios, CLI examples, and HTTP API calls, see USAGE.md.

Quick Start

From the parent repository directory, run commands from this project root:

cd uvm
./uvm.py --help
python3 -m uvm --help

The pyproject.toml file also defines an installable uvm console command. The project is an application, not a reusable Python SDK.

Requirements

  • Ubuntu or another compatible Linux host
  • Python 3.12 or newer
  • Root access for UVM commands that read or mutate the protected VM registry
  • KVM enabled and accessible at /dev/kvm
  • An x86_64 host for the default guest assets
  • Working host networking, iproute2, iptables, e2fsprogs, openssl, and openssh-client

uvm install installs the required host packages. It validates /dev/kvm before downloading or launching a microVM.

Trusted Installation

Installation requires trusted SHA-256 digests by default. Supply digests from an independently trusted source before running the privileged installer:

cd uvm

sudo env \
  UVM_FIRECRACKER_SHA256='<sha256-of-firecracker-archive>' \
  UVM_KERNEL_SHA256='<sha256-of-vmlinux>' \
  UVM_ROOTFS_SHA256='<sha256-of-rootfs>' \
  ./uvm.py install

Use --force to redownload Firecracker and guest assets when replacing an image or refreshing artifacts:

sudo env \
  UVM_FIRECRACKER_SHA256='<sha256-of-firecracker-archive>' \
  UVM_KERNEL_SHA256='<sha256-of-vmlinux>' \
  UVM_ROOTFS_SHA256='<sha256-of-rootfs>' \
  ./uvm.py install --force

For local experimentation only, the old unverified quick-start behavior is available through an explicit opt-out:

sudo env UVM_ALLOW_UNVERIFIED_DOWNLOADS=1 ./uvm.py install

An unverified install is recorded as such and cannot satisfy a later strict run. Reinstall with trusted digests to establish a verified local manifest. To create VMs from an unverified local-development install, retain UVM_ALLOW_UNVERIFIED_DOWNLOADS=1 on later create commands and on the API server process.

Commands

# Install after supplying the trusted checksum variables shown above.
# A bare `install` command fails under the default strict integrity policy.

# Create and boot a VM with the defaults: 1 CPU, 512 MiB RAM, and root/root.
sudo ./uvm.py create

# Request CPU and RAM explicitly. RAM defaults to MiB; B/K/M/G suffixes work.
sudo ./uvm.py create --cpu 1 --ram 1G

# Set credentials for an account that already exists in the guest image.
sudo ./uvm.py create --username root --password '<guest-password>'

# Select the guest IP. Despite the legacy flag name, this is the guest IP.
sudo ./uvm.py create --host-ip 10.42.0.10

# Show persisted VM state and observed process status.
sudo ./uvm.py list

# Connect over SSH by VM ID or guest IP.
sudo ./uvm.py ssh vm-<id>
ssh root@10.42.0.10

# Stop or remove a VM.
sudo ./uvm.py stop vm-<id>
sudo ./uvm.py destroy vm-<id>

SSH uses StrictHostKeyChecking=accept-new and a VM-ID-specific host-key alias by default. --insecure-host-key restores the old no-verification behavior for a single connection.

Management API

Install the project dependencies before serving the API:

cd uvm
python3 -m pip install .

Launch the FastAPI management server with the requested CLI form:

cd uvm
export UVM_API_TOKEN='replace-with-a-long-random-secret'
sudo env UVM_API_TOKEN="$UVM_API_TOKEN" \
  uvm --serve --host 127.0.0.1 --port 8000

# Equivalent when running the source checkout directly.
sudo env UVM_API_TOKEN="$UVM_API_TOKEN" \
  ./uvm.py --serve --host 127.0.0.1 --port 8000

The default bind address is 127.0.0.1 and the default port is 8000. OpenAPI documentation is available at /docs once the server is running.

The server exposes these routes:

Method Route Purpose
GET /health Unauthenticated liveness response
POST /install Install or refresh host assets; body: { "force": false }
GET /vms List VM records and observed status
POST /vms Create a VM; body supports cpu, ram, guest_ip, username, and password
GET /vms/{id-or-ip} Retrieve one VM by ID or guest IP
POST /vms/{id}/stop Stop a VM while retaining its private disk
DELETE /vms/{id} Destroy a VM and release its resources

The API intentionally does not proxy interactive SSH. Use the returned guest IP with uvm ssh or a normal SSH client.

API Security

The server controls privileged host operations. UVM_API_TOKEN is required for every API server, including loopback-only deployments. All management routes require it in the X-UVM-Token header. FastAPI's informational /docs, /redoc, and /openapi.json endpoints do not require a token.

A non-loopback bind additionally requires TLS certificate and key paths:

export UVM_API_TOKEN='replace-with-a-long-random-secret'
export UVM_API_TLS_CERT='/etc/uvm/api-cert.pem'
export UVM_API_TLS_KEY='/etc/uvm/api-key.pem'
sudo env \
  UVM_API_TOKEN="$UVM_API_TOKEN" \
  UVM_API_TLS_CERT="$UVM_API_TLS_CERT" \
  UVM_API_TLS_KEY="$UVM_API_TLS_KEY" \
  ./uvm.py --serve --host 0.0.0.0 --port 8000

Send the token on management requests:

curl \
  -H "X-UVM-Token: $UVM_API_TOKEN" \
  http://127.0.0.1:8000/vms

Do not expose this server directly to an untrusted network. For remote access, keep TLS enabled and consider placing it behind an additional authenticated reverse proxy.

Guest Image Contract

The installed kernel and rootfs are templates. create copies the rootfs to the VM runtime directory before Firecracker opens it read-write, so VMs do not share a mutable root disk.

During creation, UVM updates the private ext4 disk with a password for an existing guest account and enables OpenSSH password authentication. The defaults are username root and password root. A custom image must provide:

  • sshd
  • /etc/passwd, /etc/shadow, and /etc/ssh/sshd_config
  • The requested user account; UVM does not create missing users

The known public demo key bundled in Firecracker's default bionic image is removed from each private disk. UVM also replaces the conventional RSA, ECDSA, and Ed25519 SSH host keys in every private disk so cloned VMs have distinct host identities. Never rely on SSH private keys baked into a shared template.

Host Networking

By default, UVM manages:

Network:  10.42.0.0/24
Gateway:  10.42.0.1
Bridge:   uvm0
TAPs:     uvm-<unique suffix>

It creates the bridge when absent, validates an existing bridge before using it, enables IPv4 forwarding, adds an egress masquerade rule, and adds matching forward rules for return traffic. Guest IPs, gateway, bridge name, and guest boot netmask derive from the configured CIDR.

Persistent State

The default data directory is /var/lib/uvm:

/var/lib/uvm/
├── bin/                 # Firecracker and Jailer binaries
├── images/              # Kernel and rootfs templates
├── vms/<vm-id>/         # Per-VM disk, config, log, and API socket
├── integrity.json       # Download/build checksums and verification provenance
└── state.json           # Root-only VM inventory and guest credentials

State writes are locked and atomic. Lifecycle operations share an additional operation lock so concurrent create, stop, destroy, and install commands cannot overwrite each other's state or binaries.

The registry stores guest usernames and passwords in plaintext, so state.json is mode 0600. Treat it as a secret, do not include it in bug reports, and replace the default root password immediately.

Credential provisioning applies only to VMs created by this version. Existing VM disks are not modified automatically.

Configuration

All optional configuration is supplied through environment variables:

Variable Purpose
UVM_BASE Data directory; defaults to /var/lib/uvm
UVM_NETWORK Guest IPv4 CIDR; defaults to 10.42.0.0/24
UVM_GATEWAY Bridge/guest gateway; defaults to 10.42.0.1
UVM_BRIDGE Linux bridge name; defaults to uvm0
UVM_KERNEL_URL Guest kernel download URL
UVM_ROOTFS_URL Guest rootfs download URL
UVM_FIRECRACKER_SHA256 Trusted Firecracker release archive SHA-256
UVM_KERNEL_SHA256 Trusted guest kernel SHA-256
UVM_ROOTFS_SHA256 Trusted guest rootfs SHA-256
UVM_FIRECRACKER_BINARY_SHA256 Optional trusted local Firecracker binary SHA-256
UVM_ALLOW_UNVERIFIED_DOWNLOADS Set to 1 only for unverified local development
UVM_API_TOKEN Required for every API server; sent as X-UVM-Token
UVM_API_TLS_CERT TLS certificate path required for a non-loopback API bind
UVM_API_TLS_KEY TLS private-key path required for a non-loopback API bind

Project Layout

uvm/
├── README.md
├── pyproject.toml
├── uvm.py               # Executable compatibility launcher
├── uvm/                 # Private application implementation
│   ├── cli.py           # argparse, terminal output, SSH execution
│   ├── app.py           # Application composition root
│   ├── api_models.py     # FastAPI request and response models
│   ├── config.py        # Settings and filesystem layout
│   ├── domain.py        # Typed VM and state models
│   ├── errors.py        # Expected application failures
│   ├── integrity.py     # Checksums and manifest provenance
│   ├── install.py       # Host/artifact installation
│   ├── images.py        # Guest templates and per-VM disks
│   ├── lifecycle.py     # Create/list/stop/destroy orchestration
│   ├── network.py       # IP/MAC/TAP/bridge/NAT management
│   ├── routers/          # Health, installation, and VM HTTP routers
│   ├── server.py         # FastAPI application factory and Uvicorn launcher
│   ├── state.py         # Locked atomic JSON persistence
│   ├── system.py        # Host command and KVM adapters
│   └── firecracker/     # Firecracker config, API, and process adapters
└── tests/               # Unit tests with mocked host boundaries

Testing

From the parent repository directory, run the unit suite from this project root:

cd uvm
python3 -m pip install '.[test]'
PYTHONDONTWRITEBYTECODE=1 python3 -B -m unittest discover -s tests -v

The unit tests do not need root, KVM, Firecracker, or host network changes. They cover validation, state migration and locking, integrity manifests, network command rendering, Firecracker request ordering, process safety, and lifecycle rollback. API route tests run automatically when the FastAPI and httpx test dependencies are installed.

Current Boundaries

  • Fractional CPU requests still round Firecracker vCPUs up and are advisory.
  • stop retains the private VM disk; the current command surface does not yet include a separate start command.
  • Firecracker is launched directly by this local CLI. Jailer/cgroup hardening is not implemented by the current command set.
  • Run real KVM, TAP, and guest-connectivity checks on an isolated Linux host before relying on UVM for workloads.
S
Description
No description provided
Readme
166 KiB
Languages
Python 100%