A backend in Katakate combines a VM runtime with a storage driver. There are four: kata-firecracker-devmapper (kfd), kata-qemu-longhorn (kql), k7d, and k7d-fc. A node can install any combination (k7 install --backend kfd,kql,k7d,k7d-fc) and each sandbox picks one (k7 create --backend …).
Deprecated aliases: fd and ql (and the long names without the kata- prefix) are older names for kfd / kata-firecracker-devmapper and kql / kata-qemu-longhorn. They still resolve for backwards compatibility, but new configs should use the current names. k7-fc is accepted as an alias of k7d-fc.
At a glance#
kfd — kata-firecracker-devmapper | kql — kata-qemu-longhorn | k7d | k7d-fc | |
|---|---|---|---|---|
| Runtime class | kata | kata-qemu | k7 | k7-fc |
| Hypervisor | Firecracker (microVM) via Kata | QEMU via Kata | k7d — purpose-built in-process Rust VMM | The same k7d daemon driving stock Firecracker under the stock jailer |
| Process isolation | Jailer (chroot, dropped caps, seccomp) | Kata’s built-in sandboxing | KVM hardware isolation; one daemon hosts every VM (see k7d security model) | KVM plus a per-VM jailer (own uid, chroot, pid namespace, cgroup, Firecracker seccomp) |
| Storage driver | LVM thin-pool + containerd devmapper snapshotter | Longhorn CSI (replicated PVCs) | erofs images on virtio-blk + reflink-XFS volume images | Same as k7d (erofs / scratch on virtio-blk); no virtiofs / hostPath |
| Persistent disk per sandbox | Ephemeral (devmapper LV, lost on delete) | Yes — <sandbox>-root-lh PVC, sized via --root-disk-size | Host-local VM state | Host-local VM state |
Named snapshots (k7 snapshot) / k7 restore | ❌ Rejected up front (node-local devmapper root) | ✅ Disk-level VolumeSnapshots | ❌ Not yet exposed by K7 (fork the live sandbox instead) | ❌ (same as k7d) |
k7 fork | ❌ Rejected | ✅ Disk clone + cold boot (~45 s) | ✅ Warm CoW fork of memory and disk (~5 ms at the VMM, ~2.4 s end-to-end) | ✅ Same daemon path (Firecracker Diff snapshot → reflinked base → LoadSnapshot), ~2.5 s end-to-end |
k7 pause / resume | Scale to 0 / 1 (state lost; --snapshot rejected up front) | Scale to 0 / 1, disk persists | VM frozen in place: vCPUs stop, memory stays (0.2 s / 0.3 s) | Same daemon path (0.2 s pause; see note below) |
Docker in the VM (--docker) | ✅ Privileged docker-vehicle, overlay2 on an ephemeral LVM block LV | ✅ Privileged docker-vehicle, overlay2 on a Longhorn Block PVC (survives pause / fork / restore) | ✅ Agent-supervised in-guest dockerd, overlay2 on virtio-blk, forkable | ✅ Same guest dockerd, forkable |
| Multi-node replication | ❌ Node-local thin pool | ✅ Longhorn replicas across nodes | ❌ Host-local trees today | ❌ |
Tenant limits.network (--egress-bps / --ingress-bps) | ✅ Firecracker token buckets | ✅ tc on the tap | ❌ 400 LimitNotEnforceable (also for disk quotas; diskIO is unenforceable everywhere) | ❌ (same as k7d) |
| Cold-create latency | Faster (microVM boot) | Slower (full VM boot + PVC bind), ~17 s | ~2.1 s to a Ready pod | ~2.1 s to a Ready pod |
| Privileged container required | No (unless --docker) | Yes (for the bind-mount persistence wrapper) | No | No |
| Architecture | amd64, arm64 | amd64, arm64 | amd64 / x86_64 only | amd64 / x86_64 only |
| Best for | Many small ephemeral workloads, AI agents, CI runners | Long-lived workloads, replicated state | Fork-heavy workflows: RL rollouts, agent tree search, snapshot trees | k7d’s fork/pause lifecycle and a per-VM jailer — multi-tenant --docker |
Numbers are medians of 3 on one Hetzner AX41 node; ranges and methodology are in PERFORMANCE.md.
Privilege separation is the k7d daemon, not a fifth backend: a root node broker (no listening socket) plus a uid-k7d VM broker for the whole node. Both k7d and k7d-fc sandboxes go through that pair. Tables: k7d vs k7d-fc and privilege separation.
Choosing a backend#
Pick kata-firecracker-devmapper when:
- You run many short-lived sandboxes per node and want the smallest possible footprint.
- You don’t need to pause, resume, fork, or persist state between restarts.
- You’re on a single node and don’t need replication.
- Security is paramount: Firecracker has the smallest attack surface, and the jailer is active (verified by
tests/integration/test_firecracker.py::test_jailer_active).
Pick kata-qemu-longhorn when:
- You need a real persistent disk that survives pod restarts (
/mnt/statemount inside the sandbox). - You want to
pause/resume/forksandboxes at the disk level, ork7 snapshot/k7 restorenamed snapshots. - You run multi-node and want data to survive node failure (Longhorn replicates).
- You need bigger root disks (sized with
--root-disk-size), or a Docker graph that survives pause / fork / restore.
Pick k7d when:
- Fork latency matters: k7d warm-forks a running VM in ~5 ms with faithful memory, disk, process, and network state — versus ~45 s for a disk-only Longhorn fork.
- You want snapshot trees: fork many branches from a checkpoint, protect winners, prune losers, under RAM/disk budgets.
- Your workload is RL training or agent tree search over many resettable environments.
- You want a forkable Docker engine:
k7 create --docker --backend k7drunsdockerdinside the guest andk7 forkclones it, running inner containers included.
Pick k7d-fc when:
- You want k7d’s fork / pause lifecycle and Firecracker’s jailer around every VM — a separate VMM process per sandbox with its own uid, chroot, pid namespace, cgroup, and Firecracker’s default seccomp. A guest-to-VMM escape lands in that jail instead of the k7d daemon.
- You run
--dockerfor mutually distrusting tenants. The guest side is identical tok7d, so this is a RuntimeClass switch, not a rewrite. - You can live without virtiofs / hostPath volumes (refused up front on
k7d-fc) and without k7d’s time warp.
See the K7d documentation for the runtime’s own concepts, API, and benchmarks, and Backends: native vs Firecracker for how the k7d daemon selects the engine.
Cluster fork is a k7d feature, not a k7 feature. Forking an entire live Kubernetes cluster (~100 ms for a 3-node cluster) is done by the k7d runtime directly, through its own daemon API — see Cluster mode. k7’s fork command clones a single sandbox.
Specifying the backend#
At install time#
At sandbox creation time#
In YAML:
If the requested backend isn’t available on any node, you’ll see a clear FailedScheduling message listing which backends are.
Backend-specific behavior#
kata-qemu-longhorn#
- Each sandbox gets a Longhorn PVC named
<sandbox>-root-lh, defaulting to 10Gi (override with--root-disk-size 20Gior in YAML). - The container runs privileged to allow the persistence wrapper to bind-mount
/mnt/statefrom the PVC. - Image base must include
/bin/sh,tar, andmount(Debian/Ubuntu/Alpine all work). - Image
ENTRYPOINT+CMDare preserved by default — override with--entrypoint/--cmd. pausetakes a crash-consistent VolumeSnapshot before scaling to 0 (optional; pass--snapshot <name>).forksnapshots the source PVC, clones it via Longhorn’s CSI VolumeSnapshot+Restore, then creates a new Deployment pointing at the cloned PVC.--dockeradds a second Longhorn Block PVC (<name>-docker-lh, default 20Gi via--docker-disk) for the overlay2 graph; it is included in pause / fork / restore. The two VolumeSnapshots are crash-consistent per volume, not cross-volume atomic.
kata-firecracker-devmapper#
- Sandbox storage is a per-pod logical volume on the LVM thin-pool — fast to create, lost on delete.
- The Firecracker process runs inside the jailer: a chroot, with dropped capabilities and a restrictive seccomp filter, as an unprivileged UID.
- Kata’s guest seccomp is enforced (
disable_guest_seccomp = false): the container’sRuntimeDefaultseccomp profile is applied by the in-guest runtime, not silently dropped. pause/resumeonly scale the Deployment;fork,k7 snapshot createandk7 pause --snapshotare rejected up front — there’s no persistent disk to snapshot.--root-disk-sizehas no effect (warned by the CLI).--dockerputs the overlay2 graph on a generic ephemeral volume from StorageClassk7-docker-lvm(OpenEBS LVM LocalPV overkata-vg/thin-pool); it is deleted with the pod andk7 forkof a kfd--dockersandbox is rejected. Docker-heavy kfd nodes share the thin-pool with these LVs — sizekata_thinpool_pv_sizeaccordingly.
k7d#
- Pods run as k7d microVMs via
runtimeClassName: k7— a small containerd shim talks to thek7ddaemon over/run/k7d/k7d.sock. - Fork copies the whole VM: memory, disk, processes, and network identity — not just the disk. Warm fork is ~5 ms. With k7d 0.9.0+ the fork keeps a working NIC (own bridge, NAT), so forked sandboxes have egress like their source; older k7d forks came up without network.
- Multi-vCPU guests are supported: the vCPU count is derived from the pod’s CPU limit (
cpu: "2"boots a 2-vCPU guest), with fork/snapshot parity. - VM ops work for sandboxes on any node. The k7d daemon and its socket are node-local, but
k7 pause/resume/forkthrough the API are forwarded to thek7-agentDaemonSet pod on the sandbox’s node when needed. A fork still lands on the source’s node (cross-node fork is on the k7d roadmap). --dockerdoes not add a second container: the pod is stampedk7d.katakate.org/docker=trueand the guest agent (PID 1) supervises a pinneddockerdwhose graph lives on a per-sandbox virtio-blk scratch disk (overlay2, default 20Gi). Because it is not a CRI sidecar,k7 forkof a--dockersandbox works — the child keepsdockerdand its running inner containers. Requires k7d 0.6.0+ (payload at/usr/local/share/k7d/docker); an older k7d fails loudly withthis k7d has no docker service; upgrade.- Warm fork works for single-workload sandboxes (including
--docker); forking a sandbox with a real CRI sidecar is rejected (usekqlfor that). - Per-node k7d disk-pool utilization is visible via
k7 nodes storage(see CLI reference). - See RuntimeClass k7 for how the runtime is registered, and the feature matrix for current Kubernetes feature support.
k7d-fc#
- Same daemon, shim, guest agent, and
--dockerservice ask7d; pods useruntimeClassName: k7-fc. The daemon boots the guest on upstream Firecracker under the upstream jailer (sha-pinned binaries installed to/usr/local/binby the playbook — a different binary from Kata’s/opt/kata/binFirecracker used bykfd). k7 0.4.0+ (including 0.5.0) and k7d both pin v1.16.2; k7 0.3.1 still vendored v1.16.1. k7 install --backend k7d-fcwrites/etc/k7d/shim-k7-fc.toml(backend = "firecracker"), registers containerdruntimes.k7-fcwith aConfigPathoption (and noBinaryName), creates RuntimeClassk7-fcwith a PodOverhead of32Mi/50m, and labels the nodek7.katakate.org/backend-k7d-fc=true(plusbackend-k7d, so nativek7dpods still schedule there).- Fork and pause go through the same
/run/k7d/k7d.sockverbs andfork-source-*annotations ask7d. Under the hood a fork is a Firecracker Diff snapshot materialised onto a reflinked base image andLoadSnapshoted into a fresh jail (~2.5 s end-to-end via k7; ~2.45 s fork→exec measured). - hostPath / PVC
volumesare refused at create time — Firecracker has no virtiofs. Time warp / dilation is unavailable on this backend. - Pause / resume → exec needs Firecracker v1.16.2+, which k7 0.4.0+ installs. k7 0.3.1 still copied v1.16.1, which gated vsock RX after a bare resume (upstream #6100 / k7d CHALLENGES); current k7d then fails
k7 resumeloudly (Firecracker < v1.16.2). Nativek7dis unaffected. - Requires k7d 0.6.0+ (Firecracker engine +
ConfigPath). The stock 0.5.0 tarball does not knowConfigPathand the RuntimeClass will not come up.
