> ## Documentation Index
> Fetch the complete documentation index at: https://docs.katakate.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Backends: native and Firecracker

> One daemon, one shim, two VMMs — k7d's in-process rust-vmm engine or stock Firecracker under the stock jailer (RuntimeClass k7-fc)

k7d boots every VM through the same daemon and the same containerd shim. The **backend** is which VMM process owns the KVM fd:

* **`native`** (default) — k7d's in-process rust-vmm VMM. The VM lives in the daemon's address space; warm fork is a `MAP_PRIVATE` CoW of guest memory (\~5 ms). This is what every other k7d page describes.
* **`firecracker`** — a stock, sha-pinned upstream [Firecracker](https://github.com/firecracker-microvm/firecracker) v1.16.1 process, launched under the stock `jailer`. Same guest kernel, initramfs, agent, protocol, fork/tree/pool verbs and Docker service; the VMM is the audited upstream binary and sits in its own uid, chroot, pid namespace, cgroup and seccomp filter.

<Note>
  The Firecracker engine ships in **k7d 0.6.0** (`install.sh --with-k3s` registers RuntimeClass `k7-fc` and the `ConfigPath` runtime option). The 0.5.0 tarball does not.
</Note>

## Selecting a backend

Three knobs, one result (`VmConfig.backend`). The first one that yields a backend wins; a contradiction is a loud Create error.

| Knob                                      | Where                                                                                                                                                                                          | Native                   | Firecracker     |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | --------------- |
| `VmConfig.backend`                        | control socket `create_vm`                                                                                                                                                                     | `native` (serde default) | `firecracker`   |
| Pod annotation `k7d.katakate.org/backend` | forwarded into the OCI spec by `pod_annotations`                                                                                                                                               | `native`                 | `firecracker`   |
| RuntimeClass `k7` / `k7-fc`               | containerd handler; `runtimes.k7-fc` sets `[options] ConfigPath = /etc/k7d/shim-k7-fc.toml` (`backend = "firecracker"`) because the k3s CRI does not stamp `io.kubernetes.cri.runtime-handler` | handler `k7`             | handler `k7-fc` |

Rules:

* `runtimeClassName: k7-fc` ⇒ Firecracker, even with no pod annotation.
* `runtimeClassName: k7` ⇒ native **unless** the pod carries `k7d.katakate.org/backend: firecracker` (per-pod opt-in).
* `k7d.katakate.org/backend: native` on a `k7-fc` pod is a Create error containing `contradicts`. The RuntimeClass is never silently overridden.
* Any other annotation value fails Create — a typo must not boot a pod on the wrong engine.

Gate admission on `runtimeClassName` (`k7` vs `k7-fc`); k7d does not ship a Kyverno/OPA policy. In [K7](/k7/concepts/backends#k7d-fc) the two RuntimeClasses surface as the `k7d` and `k7d-fc` backends.

```yaml theme={null}
apiVersion: v1
kind: Pod
metadata:
  name: demo-fc
spec:
  runtimeClassName: k7-fc
  containers:
    - name: main
      image: alpine:3.20
      command: ["sleep", "infinity"]
```

## Feature matrix

| Feature                                      | Native (`k7`)                           | Firecracker (`k7-fc`)                                                 |
| -------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------- |
| VMM                                          | in-process rust-vmm                     | stock Firecracker v1.16.1 under the stock jailer (sha-pinned)         |
| Guest → host boundary                        | KVM                                     | KVM + jailer (uid, chroot, pid ns, cgroup) + Firecracker seccomp      |
| Agent                                        | vsock                                   | vsock (Unix socket inside the jail, same protocol)                    |
| Rootfs                                       | erofs on virtio-blk                     | erofs on virtio-blk (Firecracker drives)                              |
| Scratch / data disks / image slots           | virtio-blk                              | virtio-blk, filled after boot with `PATCH /drives`                    |
| Network                                      | daemon TAP + bridge                     | same TAPs, moved into the jail by name                                |
| Warm fork (CoW memory)                       | `MAP_PRIVATE`                           | Diff snapshot → `FICLONERANGE` onto a reflinked base → `LoadSnapshot` |
| Cluster fork / snapshot tree / warm pool     | yes                                     | yes (`K7D_POOL_BACKEND=firecracker` for the pool)                     |
| `k7d.katakate.org/docker` (in-guest dockerd) | yes                                     | yes — same agent service                                              |
| virtiofs / hostPath fallback                 | yes (or `K7D_DENY_VIRTIOFS_FALLBACK=1`) | **unsupported** — Create fails naming the backend                     |
| Time warp / dilation / autowarp              | yes                                     | **unsupported**                                                       |
| Fork latency (256 MiB busybox, warm)         | \~5–50 ms                               | \~190 ms for one child, \~76 ms/child at n=8                          |
| PodOverhead                                  | none                                    | `32Mi` / `50m` on the RuntimeClass                                    |

`Unsupported` is a named error, never a silent downgrade: a pod that asked for virtiofs or time warp on `k7-fc` does not reach Running.

## How a Firecracker fork works

A native tree forks by mapping the source's memory `MAP_PRIVATE`. A Firecracker tree has no such mapping, so a **point** is:

1. `PUT /snapshot/create` on the paused source — a **Diff** snapshot by default (only pages dirtied since the previous point; `K7D_FC_FORK_SNAPSHOT=full` writes all of RAM and exists for cross-checking, not as a fallback).
2. The Diff extents are `FICLONERANGE`d onto a root-owned `base.mem`; `restore-N.mem` is a `FICLONE` of the result — the point costs its dirty set, the base costs O(1).
3. Each child is a fresh jailed Firecracker `LoadSnapshot`ed from its own reflink of `restore-N.mem` (`mem_backend: File`, `clock_realtime`), with writable disks reflinked from clones taken at the same paused instant. Every child extent reports `FIEMAP_EXTENT_SHARED` — CoW is proven, not assumed.

Cluster fork runs that primitive on every member after a **parallel** `PATCH /vm Paused` (pause skew p99 ≈ 125 µs across 20 runs). Suspend exports a Full point out of the jail and drops the process; resume re-stages a jail and `LoadSnapshot`s with `network_overrides` so the member keeps its TAP identity.

Measured on an exclusive Hetzner node (2026-09-08, 256 MiB / 1 vCPU busybox guests, 500 ms per-child ceiling asserted in tests):

| Operation                                          | Typical                                     |
| -------------------------------------------------- | ------------------------------------------- |
| Root point at `tree_create` (first Diff, 43 MiB)   | 169 ms                                      |
| `prepare_fork_source` (Diff, \~1–2 MiB dirtied)    | 140–157 ms                                  |
| `fork_batch` n=1                                   | **190 ms**, child answers `ping` 5 ms later |
| `fork_batch` n=8                                   | 607 ms = 76 ms per child                    |
| `rollback` (load the existing point)               | 75–90 ms                                    |
| Cluster `fork_batch`, 3 members                    | \~0.67 s (debug build)                      |
| Cluster suspend / resume (2 members)               | \~2.1 s / \~512 ms (debug build)            |
| Warm pool acquire (`K7D_POOL_BACKEND=firecracker`) | \~100 ms                                    |

Most of the per-child cost is staging a chroot, leasing a uid, spawning `jailer` (new namespaces, `pivot_root`, cgroup) and waiting for its API socket — the warm pool of pre-staged jails is what recovers it. See [`LATENCY_BUDGETS.md`](https://github.com/Katakate/k7d/blob/main/LATENCY_BUDGETS.md) for the native rows.

## What the jail buys, and what it does not

|               |                                                                                                                                                                                                                                                                                                                                                                    |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Gained**    | The VMM is out of the daemon's address space. A guest → VMM escape lands in a per-VM uid, chroot, pid namespace, cgroup and Firecracker's seccomp filter — not in `k7d`.                                                                                                                                                                                           |
| **Unchanged** | The daemon is still host root and still one process (control socket, TAP/bridge, vsock proxy, tree bookkeeping). The shim still rewrites the OCI spec, and the [guest-controlled path rules](/k7d/concepts/security#guest-controlled-paths) are the floor for both backends. The guest agent is the same binary. Sibling forks of one tree still share the daemon. |
| **Lost**      | Time warp, dilation, autowarp. virtiofs / hostPath (`backend firecracker has no virtiofs`). Native `MAP_PRIVATE` fork latency.                                                                                                                                                                                                                                     |

Multi-tenant `--docker` should use `k7-fc`: the guest side is identical, so it is a RuntimeClass switch, not a rewrite. The VMM jail is the extra boundary; the in-guest `docker.sock` is still guest root. See [Security](/k7d/concepts/security#firecracker-profile-k7-fc).

## Installing

* `packaging/install.sh --with-k3s` registers **both** `k7` and `k7-fc` (containerd blocks without `BinaryName`, `/etc/k7d/shim-k7-fc.toml`, RuntimeClass `k7-fc` with `overhead.podFixed` 32Mi / 50m).
* `utils/install-firecracker.sh` puts the pinned `firecracker` and `jailer` in `/usr/local/bin`, verifying every sha256 from `guest/fc/pins.env`. A digest mismatch is fatal. `k7d doctor` reports the version and sha it finds.
* Daemon knobs: `K7D_FC_CHROOT_BASE` (jail root; default `/var/lib/k7d/fc`, a link onto the reflink XFS that holds the writable disks so they can be hard-linked into a jail), `K7D_FC_UID_BASE` (first uid of the per-VM lease range; default 60000, 1024 live VMs per daemon, exhaustion is an error rather than a wrap-around), `K7D_FC_FORK_SNAPSHOT` (`diff` default, `full`), `K7D_POOL_BACKEND` (`native` | `firecracker` for the warm pool).

Known issue: K7 observed CRI `exec` into a `k7-fc` pod hanging after a `k7 pause` / `k7 resume` cycle (readiness probe timeout, K7 [PERFORMANCE.md](https://github.com/Katakate/k7/blob/main/PERFORMANCE.md)). Native `k7` is unaffected.

## Reference

* [`docs/backends.md`](https://github.com/Katakate/k7d/blob/main/docs/backends.md) — selection matrix, source of truth
* [`OVERHEAD.md`](https://github.com/Katakate/k7d/blob/main/OVERHEAD.md) — how the `k7-fc` PodOverhead was measured
* [RuntimeClass k7](/k7d/guides/runtime-class) — installing the runtime, annotation reference
