A secure, tileable mini cloud you run yourself. Every workload gets its own Firecracker microVM, locked down with jailer. Manage everything from your terminal.
Pre-alpha: the single-node VM lifecycle (
node install,runfrom an OCI image or a rootfs,image,ps,top,logs,stop,rm,park,wake,bench,node uninstall) works.ssh,dband the multi-node control plane (server,node join) are listed in--helpbut exit with "not yet implemented".
Needs a Linux x86_64 host with KVM (/dev/kvm) and cgroup v2, and systemd
(the Node agent runs as a systemd unit).
cargo build --release -p cirrocumulus
sudo install -m 755 target/release/cirro /usr/local/bin/cirroSet up the host once, as root. This checks prereqs, fetches and verifies the
pinned Firecracker, jailer and kernel release, creates the cirro group and
state dir, and installs and starts the Node agent as a systemd unit:
sudo cirro node installThen boot a VM from an OCI image, and reach it at the address it prints:
cirro run --name web nginx:alpine
curl http://10.77.0.2/The CLI pulls the image and builds its rootfs as you, not as root, and caches it
in ~/.cache/cirro/images. A Firecracker rootfs from a Docker
image explains how, and its limits: public
linux/amd64 images only, for now. cirro run also boots an ext4 rootfs you
built yourself, given a path and a command:
cirro run --name web ./rootfs.ext4 -- /entrypoint.
cirro run talks to the Node agent over its socket (/run/cirro/agent.sock
by default, --socket/$CIRRO_SOCKET to override) and prints the VM's
address on success.
cirro run --name <name> [--mem 256M] [--vcpus 1] [-e KEY=VALUE]... [-w DIR] [-u UID:GID] \
<image> [-- <command> [args...]]
cirro run --name <name> [flags...] <rootfs> -- <command> [args...]
cirro image pull <image> # pull and build ahead of time
cirro image ls # cached images
cirro image rm <image|digest>
cirro ps [--all] # list VMs (--all includes Ended VMs)
cirro top [--once] # live CPU, memory, disk and network use
cirro logs [--follow] <name> # a VM's console log
cirro stop [--force] [--timeout <secs>] <name>
cirro rm <name> # delete an Ended VM's record and log, or a parked VM's snapshot
cirro park <name> # snapshot a VM to disk and free its RAM
cirro wake <name> # start it again from its snapshot; prints its VM address
cirro bench [--runs 10] <image|rootfs> [-- <command>] # time boot, park and wake
cirro node uninstall [--force] # reverse `node install`Run cirro --help or cirro <command> --help for the full flag list.
cirro park pauses a VM, writes a full Firecracker snapshot of its memory and
devices, and ends it, freeing its RAM; the snapshot and the VM's rootfs stay in
the state dir. cirro wake starts a new VM under the same name from that
snapshot, so the guest carries on where it was rather than booting again. It may
get a different VM address: every guest has the same Guest address inside its
own namespace (ADR 0003), and the same
tap MAC (ADR 0005).
cirro bench --runs 50 on an i9-13900H laptop (20 threads, 16 GiB, NVMe,
btrfs, Linux 7.2), with a 256 MiB guest running the test suite's counter
HTTP server, timing each request as the CLI sees it. A boot includes the 2 s
the agent waits to see a new VM stay up, so the boot itself is about 1.1 s;
park and wake have no such wait.
| Operation | p50 | p99 |
|---|---|---|
| boot | 2916 ms | 2947 ms |
| park | 274 ms | 1854 ms |
| wake | 43 ms | 54 ms |
Wake meets the goal of a 100 ms p99. A profiled wake (p50 of 20) spent about
10 ms loading and resuming the snapshot in Firecracker; the rest is the host
side. Firecracker takes about 25 ms to start under jailer, and the VM's network
namespace and links (two ip -batch runs, sysctl and nft) are set up
while it does, so that start is most of a wake. Then come moving the snapshot
into the jail (under 1 ms), recording the VM, which doesn't wait for the disk
(ADR 0006), and the CLI's round
trip to the agent (a few ms).
Park's tail is writing 256 MiB of guest memory to disk: on this btrfs it sometimes took over a second, and did so before these wake changes too (981 ms p99 over 10 runs of the earlier build).
Needs Linux, rustup (the toolchain and musl target are pinned in
rust-toolchain.toml), and /dev/kvm for the VM tests.
scripts/ci/install-hooks.sh # once: commit-msg + pre-push hooks
scripts/ci/local.sh # the same gates CI runs (--quick skips docs + tests)All gates in .github/workflows/ci.yml block merging, including commit-message
rules and a check that every commit builds. The real-Firecracker tests only
compile in CI, so run them locally. crates/cirro/tests/node_agent.rs explains the
one sudoers rule they need, and scripts/step0/README.md covers fetching the
kernel.
