Example **Firecracker VM runner** that uses a container image reference as the guest root filesystem, loading files **on demand** at runtime over HTTP range requests into gzip-compressed OCI layers.
This implements the lazy-pull approach described in [dagdotdev registry explorer](https://github.com/jonjohnsonjr/dagdotdev/blob/main/pkg/explore/README.md):
1. Build a **gzip zran-style index** (`indexed_deflate`) over each `tar+gzip` layer.
2. Record a **tar table of contents** mapping paths to uncompressed byte offsets.
3. On file read, fetch only the **compressed byte range** needed from the registry, seek in the gzip stream, and return file bytes.
4. Serve the merged overlay via **virtio-fs** using [`vhost-user-backend`](https://crates.io/crates/vhost-user-backend) + [`fuse-backend-rs`](https://crates.io/crates/fuse-backend-rs).
**Important:** virtio-fs needs two things that official Firecracker release artifacts do not provide out of the box:
1. A **guest `vmlinux` with `CONFIG_VIRTIO_FS=y`** — the CI kernels from S3 do not enable virtio-fs.
2. A **Firecracker binary with generic vhost-user** ([PR #5773](https://github.com/firecracker-microvm/firecracker/pull/5773)) — release builds only expose vhost-user **block**, not virtio-fs.
The scripts under `scripts/` automate both builds into `.deps/`.
| `fc_cache_dir_bytes_on_disk` | Local cache size (blobs + indexes) |
```bash
curl -s localhost:9100/metrics | rg '^fc_'
```
## Which VMM can use this? (virtio-fs frontends)
`fc-vhostfsd` is a **vhost-user backend**. Something in the VMM must act as the **vhost-user frontend** and connect to `--socket`. Your options:
| Frontend | Status | Notes |
|----------|--------|-------|
| **Firecracker** (generic vhost-user) | Needs recent build | [PR #5773](https://github.com/firecracker-microvm/firecracker/pull/5773) adds `PUT /vhost-user-devices/{id}` so virtio-fs works without native Firecracker virtio-fs code. `fc-runner` targets this API. |
| **Cloud Hypervisor** | Works today | First-class virtio-fs + vhost-user; point `--socket` at the same path. No Firecracker-specific API. |
| **QEMU** | Works today | `virtiofsd` / custom daemon via `-chardev socket` + `vhost-user-fs-pci` device. |
| **crosvm** | Works today | Can run vhost-user fs backends against a virtio-fs device. |
| **Stock Firecracker (released)** | No virtio-fs | Only block/net/vsock unless you build from the generic vhost-user branch. |
**You do not need to change `fc-vhostfsd` between these** — only the VMM configuration differs. `fc-runner` is Firecracker-specific; for Cloud Hypervisor or QEMU, run `fc-vhostfsd` manually and wire the socket in that VMM's config.
That kernel is fine for block-device rootfs smoke tests, but its config has `# CONFIG_VIRTIO_FS is not set`. Use `./scripts/build-vmlinux-virtiofs.sh` instead.
This clones Firecracker, enables `CONFIG_VIRTIO_FS=y` in the `6.1` CI guest config, and runs `./tools/devtool build_ci_artifacts kernels 6.1`.
Minimum kernel options (already present in Firecracker CI configs except virtio-fs):
-`CONFIG_VIRTIO_MMIO=y`
-`CONFIG_VIRTIO_FS=y`
-`CONFIG_FUSE_FS=y`
-`CONFIG_SERIAL_8250_CONSOLE=y` for `console=ttyS0`
Manual build details: [Firecracker rootfs and kernel setup](https://github.com/firecracker-microvm/firecracker/blob/main/docs/rootfs-and-kernel-setup.md).
## Firecracker binary
### For virtio-fs (`fc-runner`)
Released binaries from [GitHub releases](https://github.com/firecracker-microvm/firecracker/releases) do **not** include `PUT /vhost-user-devices/{id}` yet. Build from PR #5773:
Without `--dry-run`, `fc-runner` configures Firecracker via its HTTP API:
-`PUT /boot-source`
-`PUT /machine-config`
-`PUT /vhost-user-devices/rootfs` (virtio-fs via generic vhost-user frontend)
-`PUT /actions` (`InstanceStart`)
Guest kernel cmdline includes `rootfstype=virtiofs root=/ root=rootfs`.
## Tests
```bash
cargo test
```
Integration test `vhost_user_virtiofs_daemon_handshake` verifies the vhost-user protocol end-to-end against a local fixture layer (no registry/KVM required).
## Benchmarks
```bash
cargo bench -p fc-oci-fs
```
Benchmarks build a synthetic `tar.gz` layer with hundreds of files and measure:
- **read_last_file_1kb** — random read of the last tar entry (exercises gzip seek + range-style decompression)
- **lookup_opt_data_file** — FUSE lookup through the overlay
## Design notes & limitations (example code)
This is an **example** implementation, not production hardened: