knaller is a Go library and CLI that makes it simple to run Firecracker microVMs.
It starts a Firecracker process for each VM, connects to its API socket, configures
the VM, and boots it. VMs are non-interactive — connect via SSH. It provides a
high-level API (knaller.Run, knaller.List, knaller.StopVM, vm.Cleanup) that
abstracts away the low-level Firecracker socket API. There's also a low-level
firecracker sub-package for direct API access.
knaller/
vm.go High-level API: Run(), List(), VM type
config.go Config struct with defaults and validation
network.go Network config derivation + pasta namespace setup script
disk.go Per-VM rootfs copy management + host DNS detection
Containerfile_guest Guest rootfs container definition (Ubuntu + sshd + systemd)
Makefile Build targets: build, test, create-guest
firecracker/
client.go Low-level HTTP-over-Unix-socket Firecracker API client
models.go Firecracker API types (BootSource, Drive, etc.)
cmd/knaller/
main.go CLI binary entry point + subcommand dispatch
internal/cli/
start.go "knaller start" subcommand (non-interactive, SSH access)
stop.go "knaller stop" subcommand
list.go "knaller list" subcommand
version.go "knaller version" subcommand (version set via ldflags)
-
No state files. VM discovery is done by scanning the socket directory (
~/.local/share/knaller/sockets/) and querying each Firecracker instance via its API. The socket's existence IS the state. -
SSH access only. VMs don't have an interactive serial console. Firecracker's stdin is not connected. Use SSH to interact with the guest. The guest IP is printed on start and available via
knaller list. -
Rootless networking via pasta. Each VM runs inside a pasta network namespace (from the passt project). pasta creates a user+network namespace with a TAP device and provides L2↔L4 translation to the host — all without root privileges. Inside the namespace, a second TAP device is created for Firecracker's guest NIC using
ip tuntap(works because we have CAP_NET_ADMIN within the namespace). -
Per-VM rootfs copies. Each VM gets its own copy of the base rootfs at
~/.local/share/knaller/vms/<name>/rootfs.ext4, usingcp --reflink=autofor copy-on-write when the filesystem supports it. -
Auto DNS via kernel boot args. DNS servers are passed to the guest via the kernel
ip=boot parameter. The guest rootfs symlinks/etc/resolv.conf→/proc/net/pnpwhere the kernel writes them. Host DNS detection skips localhost entries (systemd-resolved stub) and falls back toresolvectl dnsor 1.1.1.1/8.8.8.8. -
One Firecracker process per VM. Firecracker is not a daemon — each process is exactly one VM with one API socket. Knaller starts a new Firecracker process for each
Run()call and manages its lifecycle. -
Cleanup is explicit. Call
vm.Cleanup()aftervm.Wait()returns. This removes the API socket and rootfs copy. Network namespace cleanup is automatic when the pasta process exits. The CLI handles this automatically via signal handlers.
go build -o knaller ./cmd/knallergo test ./... # unit tests (no root needed)
go vet ./... # static analysisUnit tests use mock HTTP servers over Unix sockets — they don't need Firecracker, root access, or KVM. Integration testing requires running actual VMs (root + KVM).
Releases are handled by GoReleaser via GitHub Actions. Push a tag to trigger a release:
git tag v0.1.0
git push origin v0.1.0This cross-compiles for linux/amd64 and linux/arm64, generates a changelog from commit messages, and creates a GitHub release.
- No external Go dependencies (only stdlib)
- Linux with KVM (
/dev/kvm) - pasta binary (from the passt project) — rootless networking
- Firecracker binary (linux/amd64 or linux/arm64)
- No root privileges required
- Guest DNS doesn't work: The host's /etc/resolv.conf pointed to 127.0.0.53
(systemd-resolved). We detect this and use
resolvectl dnsto find real upstreams. - apt interactive prompts in guest: Use
DEBIAN_FRONTEND=noninteractiveand dpkg force-confdef/force-confold options.