A Firecracker-based microVM platform in Java 25: boot VMs, snapshot them, and fork running state in milliseconds. Content-addressed snapshot storage, per-VM host networking, a vsock guest agent, a REST daemon, and a CLI, built without web frameworks on the JDK's own HTTP server with virtual threads.
Verified end to end against real Firecracker (KVM, 1 vCPU, 128MiB guests, Ubuntu 22.04 rootfs with the agent JVM inside):
| Path | Time | What it includes |
|---|---|---|
| Cold boot to healthy agent | 2083 ms | Kernel boot (~150ms), systemd, agent JVM startup in the guest |
| Snapshot restore to healthy agent | 91 ms | Memory restored with the JVM already warm; 23x faster than cold boot |
The restore path is the point of the platform: forking N copies of a running VM costs ~91ms each plus a disk copy, and concurrent forks of one snapshot are exercised both live (3 forks) and in the stub suite (50 forks on virtual threads, all unique IPs, all RUNNING within 5s).
ignitionctl (CLI, Picocli) ignition-sdk (images + typed client)
\ /
| HTTP |
v v
ignition-api: IgnitionDaemon (JDK HttpServer, virtual threads)
VmService / SnapshotService problem+json, /metrics, state.json
| | |
v v v
ignition-firecracker ignition-registry ignition-network
FirecrackerHttpClient SHA-256 blob store IPAM (SQLite)
VmLifecycleService SQLite metadata TAP + NAT via ip/iptables
VsockServer push/pull/fork/GC rollback + teardown
| |
v all modules share v
firecracker <- ignition-domain (pure model) -> host bridge
processes MicroVm, Snapshot, VmStatus
| lineage, NetworkConfig
v vsock
ignition-agent (in-guest, Java 21, JNA, 2.6MB jar)
| Module | Role | Details |
|---|---|---|
| ignition-domain | Pure domain model, zero dependencies | State machine, lineage resolution, validated records |
| ignition-firecracker | VMM adapter | HTTP/1.1 over Unix sockets, process launcher + disk staging, lifecycle orchestration, vsock client |
| ignition-registry | Snapshot storage | Content-addressed blobs, SQLite metadata, reference-counted GC |
| ignition-network | Host networking | IPAM, TAP devices, NAT, transactional setup |
| ignition-api | Daemon + CLI | REST API, services, config, graceful shutdown, ignitionctl |
| ignition-agent | Guest agent | vsock listener inside the VM, network/env/exec/health |
| ignition-sdk | Deployment SDK | Code-first ext4 image building (debugfs, no root), typed REST client |
| ignition-examples | Runnable SDK examples | Deploy loop, image init as code (python-base), fork farm |
Starting from a blank server (VirtualBox VM, cloud instance, bare metal)? One command sets up everything, KVM check to running daemon:
curl -fsSL https://raw.githubusercontent.com/surajkumar/Ignition/main/scripts/install.sh | sudo bashdocs/GETTING-STARTED.md explains what it
does and walks the same path by hand: KVM/nested virtualization,
assets, the ignition-base image (stock rootfs + Java runtime + guest
agent, built without root by scripts/build-base-image.sh), the host
bridge, the daemon, and a first deployed application. The sections
below assume a working dev machine.
- JDK 25+ (the build targets
--release 25; the agent targets 21) - Linux for anything that touches real VMs; the build and the 244-test suite run anywhere
./gradlew build # compile everything, run all 244 tests
./gradlew :ignition-api:ctlJar # standalone ignitionctl jar
./gradlew :ignition-agent:fatJar # guest agent jar (2.6MB)
./gradlew :ignition-sdk:sdkJar # self-contained deployment SDK jarNo root, KVM, or Firecracker needed for any of the above: the entire platform is testable against in-memory stubs.
./gradlew :ignition-api:run
# or build a distribution with start scripts:
./gradlew :ignition-api:installDist
ignition-api/build/install/ignition-api/bin/ignition-apiIt binds 127.0.0.1:7777. Configure via system properties or an
application.properties file (data dir, subnet, bridge, kernel and
rootfs paths, Firecracker retry/timeout, shutdown grace); every key is
documented in IgnitionConfig. Production use needs root (TAP/NAT), a
host bridge, Firecracker on PATH, and a kernel plus agent-equipped
rootfs, exactly as in the live test setup below.
alias ignitionctl='java -jar ignition-api/build/libs/ignitionctl-1.0-SNAPSHOT.jar'
ignitionctl deploy --name billing --artifact app.jar --env APP_ENV=prod --run
ignitionctl ps
ignitionctl logs <vm-id> --unit billing
ignitionctl exec <vm-id> systemctl status billing
ignitionctl snapshot <vm-id> --name checkpoint
ignitionctl fork --vm <vm-id> --name clone-1
ignitionctl apps
ignitionctl push --mem vm.mem --disk vm.disk --name golden
ignitionctl run --snapshot golden --name worker-1
ignitionctl images --tree
ignitionctl pull <snapshot-id> --dest ./backups
ignitionctl kill <vm-id> # stop; record kept
ignitionctl rm <vm-id> # remove entirely (record + workspace)Point it at a remote daemon with IGNITION_HOST=host:port.
Requires Linux, KVM (/dev/kvm), and root. Three steps:
# 1. Fetch firecracker, a kernel, and a rootfs (idempotent, checksummed)
./scripts/download-test-assets.sh
# 2. Bake the guest agent + a jlink Java runtime into the rootfs
# (exact validated commands in scripts/README.md)
# 3. Run
sudo JAVA_HOME=$JAVA_HOME ./gradlew liveTestIf anything is missing, the tests skip and print the exact fix. The suite cold-boots a VM, verifies the agent over vsock, snapshots, restores, forks concurrently, and proves host-to-guest networking, then cleans up TAPs, processes, and addresses even on failure. Full details in scripts/README.md.
- 244 unit and integration tests run without root, KVM, or Firecracker
(
./gradlew test): wire-level VMM and vsock protocol tests against fake Unix-socket servers, full REST lifecycle over real HTTP, a 50-VM fork storm, registry round-trips, IPAM persistence, graceful shutdown, and SDK ext4 image round-trips via e2fsprogs (those two skip cleanly where it is not installed) - 3 live tests against real Firecracker (
./gradlew liveTest), which caught a real bug the stubs could not: Firecracker ignoresConnection: closeand sends204withoutContent-Length, so a naive read-to-EOF hangs
- No Spring, no Quarkus: the daemon is
com.sun.net.httpserverplus Jackson, one virtual thread per request - Errors are RFC 9457 problem+json; logs are structured JSON with
vmId/snapshotIdMDC;/metricsserves Prometheus text - Snapshots are content-addressed (SHA-256), so forks share blobs and deletion garbage-collects only unreferenced content
- Each VM gets a working directory with cwd-relative disk and vsock paths, which is what lets many forks restore from one snapshot without the jailer
- The guest agent avoids reflection and frameworks entirely to keep startup negligible and the jar at 2.6MB