12 KiB
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, andopenssh-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.
stopretains the private VM disk; the current command surface does not yet include a separatestartcommand.- 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.