Chapter 22: MicroVMs As Containers
Every runtime that runs a container on Linux eventually calls execve. The
process on the other end of that call is still subject to the same host kernel,
the same syscall table, and the same kernel bugs as everything else on the node.
The namespace and cgroup boundaries are real, but they are software: a kernel
vulnerability can walk past them. The microVM exists precisely to put a hardware
boundary between the workload and the host — a second kernel, a separate address
space enforced by EPT rather than by Linux's own memory management. The question
this chapter answers is what happens when you want both: the operational
interface of a container — the OCI image, the CRI API, the Kubernetes pod spec
— wrapped around the isolation of a virtual machine.
Three projects answer that question in different ways. firecracker-containerd replaces the runC shim with a Firecracker VMM while keeping the containerd daemon and its entire API surface intact. Kata Containers does the same at the CRI level, routing every Kubernetes pod into a dedicated VM, transparent to kubelet. flintlock takes the idea one level up: instead of running a container inside a microVM, it provisions a microVM that is a Kubernetes node, managed through an OCI-friendly API and controlled by a Cluster API provider. The first two care about what runs inside the VM; the third cares about the VM as the unit of infrastructure.
The place where all three projects touch the containerd book is the containerd shim v2 protocol. Understanding that handoff is the key to understanding everything else.
The Shim v2 Protocol As The Pivot Point
Three radically different systems — runC containers, Firecracker microVMs, and QEMU-backed Kata pods — appear identical to containerd above a single protocol boundary. That boundary is the shim v2 protocol, and understanding it is what makes the rest of this chapter legible.
When containerd starts a task, it does not call runC directly. It finds a shim
binary, forks it, and from that point forward speaks the
containerd.task.v2.Task ttrpc service defined in
api/runtime/task/v2/shim.proto. The shim is responsible for everything below
that service boundary: creating the container, managing its lifecycle, mounting
its rootfs, and forwarding stdio. Containerd does not care what the shim
actually does. It calls Create, Start, Kill, and Delete over ttrpc and
trusts the shim to make it happen.
The runtime name in the container spec determines which shim binary runs.
Containerd takes the last two dot-separated components of the runtime name,
replaces every . with - within those components, and prepends
containerd-shim-. The runtime name io.containerd.runc.v2 yields
containerd-shim-runc-v2; aws.firecracker yields
containerd-shim-aws-firecracker; io.containerd.kata.v2 yields
containerd-shim-kata-v2. Starting with containerd 1.6.0, the binary path can
be given directly instead of relying on the derivation.
The shim service has seventeen RPC methods: State, Create, Start,
Delete, Pids, Pause, Resume, Checkpoint, Kill, Exec,
ResizePty, CloseIO, Update, Wait, Stats, Connect, and Shutdown.
The shim must publish four events in strict order — TaskCreateEventTopic,
TaskStartEventTopic, TaskExitEventTopic, TaskDeleteEventTopic — and is
responsible for mounting the container rootfs into the rootfs/ subdirectory of
the OCI bundle path it receives in CreateTaskRequest. In containerd 2.3 and
later, the shim reads a protobuf BootstrapParams message from stdin and writes
a BootstrapResult to stdout. Legacy shims write a JSON object like
{"version": 2, "address": "/path", "protocol": "grpc"} to stdout.
Everything above the shim — container creation, image pulling, snapshot
management, event routing, the CRI API — is identical whether the shim is
containerd-shim-runc-v2 or containerd-shim-aws-firecracker. The hardware
boundary lives entirely below the seventeen ttrpc methods.
firecracker-containerd
One Shim, One VM, Many Containers
The naive mapping from containerd's task model to Firecracker would fork one
VMM per container. That is the wrong answer: each Firecracker process carries
its own memory overhead, its own guest kernel boot, and its own vCPU threads.
firecracker-containerd instead multiplexes: one shim process manages all
containers within a single microVM. The first Create call for a given VM ID
starts the shim and boots the VMM; subsequent tasks in the same VM reuse the
running instance. Containerd locates an existing shim for a VM via pid and
socket files at /var/lib/firecracker-containerd/shim-base/<vm_id>/. If the
client supplies no VM ID, the shim generates a UUID v4, giving the default
topology of one container per VM.
The project ships four components: the control plugin (compiled directly
into a specialized containerd binary — not a standalone process or a
socket-based plugin), the host-side runtime shim
(containerd-shim-aws-firecracker), the in-VM agent, and a root filesystem
image builder. The control plugin manages microVM lifecycle and exposes an API
modeled on the Firecracker lifecycle, defined in proto/firecracker.proto. Its
configuration types — FirecrackerMachineConfiguration (fields: CPUTemplate,
HtEnabled, MemSizeMib, VcpuCount), CNIConfiguration (NetworkName,
InterfaceName, BinPath, ConfDir, CacheDir, Args), and
FirecrackerDriveMount (HostPath, VMPath, FilesystemType, Options,
RateLimiter, IsWritable, CacheType) — let the caller declare the VM's
hardware before any container is created.
VSOCK: The Control Plane Across The Hardware Boundary
The host shim connects to the in-VM agent over AF_VSOCK using the ttrpc
protocol. Two port constants govern this, both confirmed in runtime/service.go:
defaultVsockPort is 10789, used for the ttrpc control channel, and
minVsockIOPort is 11000 (a uint32), used as the base port for stdio
multiplexing. Stdio for each container in the VM occupies three consecutive
ports starting at minVsockIOPort: stdin on port 11000, stdout on 11001, stderr
on 11002; a second container gets 11003–11005, and so on.
The VSOCK device in the Firecracker Go SDK is configured with ID: "agent_api",
a GuestCid cast from uint32 to int64, and a UdsPath pointing to a
host-side Unix domain socket. Firecracker's VSOCK multiplexing protocol adds one
indirection: to reach a given port in the guest, the host connects to the UDS
path and sends the string "CONNECT <port_num>\n"; Firecracker responds with
"OK <assigned_hostside_port>\n". Guest-to-host connections work in the
opposite direction: the guest targets CID 2 (the host CID), and Firecracker
forwards the connection to <uds_path>_<port_number> on the host.
Block Devices, Not Filesystems
The snapshotter is an out-of-process gRPC proxy plugin that implements
containerd's snapshotter API. firecracker-containerd ships two implementations.
The naive snapshotter does a full file copy per snapshot — useful as a proof
of concept but produces no deduplication. The devmapper snapshotter uses
device-mapper thin provisioning for copy-on-write layering, matching what
production systems need. The thin pool is named fc-dev-thinpool with
base_image_size = "10GB" and metadata rooted at
/var/lib/firecracker-containerd/snapshotter/devmapper. Containerd identifies
it as io.containerd.snapshotter.v1.devmapper in config.toml.
The important design choice is that snapshots are exposed to Firecracker as
block devices. There is no virtiofs, no 9p, no filesystem-level sharing
across the hardware boundary. The host exports a device-mapper thin volume; the
guest kernel mounts it directly. This is a deliberate constraint: virtiofs
requires a shared memory region and a dedicated virtiofsd process, introducing
host-kernel surface area that firecracker-containerd's threat model rules out.
Firecracker has no hot-plug for block devices. Every drive must be declared
before the VM boots. The runtime solves this with drive stub pre-allocation:
before boot it attaches sparse stub files (minimum 128 bytes) or /dev/null
aliases to reserve drive IDs. When a container is created and its snapshot is
ready, the runtime calls PATCH /drives/{drive_id} — the Firecracker REST
endpoint PatchGuestDriveByID — to swap the stub for the real block device.
The mountDrives function at around line 821 of runtime/service.go iterates
s.driveMountStubs and calls PatchAndMount on each before the in-VM agent
becomes available. Inside the VM, the agent correlates drives to containers
either by position (lsblk sorted by MAJOR:MINOR) or by content (a drive ID
written into the stub file that persists on the real device).
The Network: tc-redirect-tap
Network configuration in firecracker-containerd happens at VM-creation time, not
per-container. The caller passes a CNIConfiguration to CreateVM; the runtime
runs a CNI chain of three plugins. First, ptp creates a veth pair inside a
dedicated network namespace. Second, host-local handles IPAM. Third,
tc-redirect-tap — a custom Firecracker plugin — creates a TAP device inside
that same network namespace and installs Linux TC (Traffic Control) U32 filters
to redirect traffic bidirectionally between the TAP and the veth at the
ingress/egress filter level.
This is the same TAP-plus-TC pattern Chapter 21 describes for raw Firecracker
networking, but the CNI chain wraps it so the IP address and network plumbing
come from the standard CNI IPAM path rather than being hardcoded. The Firecracker
Go SDK creates and manages the network namespace, invokes CNI within it, starts
the VMM inside that namespace, and handles CNI teardown on VM termination. Inside
the guest, the IP and DNS arrive via Linux kernel boot parameters; the guest
/etc/resolv.conf is symlinked to /proc/net/pnp. The sample network is named
"fcnet" with interface name "veth0"; CNI config lives under
/etc/cni/conf.d and plugin binaries under /opt/cni/bin.
The In-VM Agent And Boot Args
The in-VM agent must be embedded in the root filesystem image and configured to
start on boot. Once running, it accepts control instructions from the host shim
over the VSOCK ttrpc channel, invokes standard Linux containers via
containerd-shim-runc-v1 (runC v1, not v2) inside the VM, emits events and
metrics back to the shim, and proxies stdio over the per-container VSOCK port
triples. The production kernel command line used with firecracker-containerd is:
The init=/sbin/overlay-init argument indicates a custom init binary that
handles the overlay filesystem setup before handing control to the agent — the
agent may be overlay-init itself or a child it launches. The pci=off
nomodules arguments reflect Firecracker's stripped device model: without PCI
and with a pre-configured driver set baked into the kernel, module loading is
neither needed nor possible.
The containerd socket for firecracker-containerd lives at
/run/firecracker-containerd/containerd.sock, with state at/run/firecracker-containerdand content root at/var/lib/firecracker-containerd/containerd. The CRI plugin is explicitly disabled inconfig.toml:disabled_plugins = ["io.containerd.grpc.v1.cri"]. This is not the containerd instance kubelet talks to; it is a dedicated daemon for the Firecracker integration.
Kata Containers
The Same Shim Protocol, A Different VM Strategy
Kata Containers ships as a single binary, containerd-shim-kata-v2, derived
from the runtime handler string io.containerd.kata.v2 by the same naming
convention. Like firecracker-containerd, one shim instance manages all
containers in a single pod's VM — there is no per-container shim fork. Unlike
firecracker-containerd, Kata targets the Kubernetes CRI path directly. Kubernetes
RuntimeClass (stable since Kubernetes 1.12) routes pods to
containerd-shim-kata-v2 via runtimeClassName: kata in the pod spec.
The CRI runtime signals sandbox versus container membership to the shim via an
OCI annotation: io.kubernetes.cri.container-type when containerd is the CRI
runtime (the primary path in this book), or io.kubernetes.cri-o.ContainerType
when CRI-O is. Both carry values sandbox or container.
The mapping is clean: each Kubernetes pod becomes one Kata VM, and each
container in that pod becomes one process inside that VM. The API boundary above
the shim — the CRI RunPodSandbox and CreateContainer calls — remains
identical to what kubelet sends for a runC pod. The VM appears nowhere in the
Kubernetes API.
kata-agent: Rust, PID 1, 43 Methods
The in-VM agent for Kata, kata-agent, has been written in Rust since Kata 2.0.
It runs as a long-running process inside the VM and is responsible for the full
container lifecycle within that VM. When the guest image is an initrd,
kata-agent runs as PID 1 at /sbin/init, built with AGENT_INIT=yes. When
the guest image is a rootfs, systemd starts as PID 1 and launches kata-agent
as a systemd service. The agent listens on vsock address vsock://-1:1024
(VMADDR_CID_ANY on port 1024), accepting connections regardless of the guest
CID the hypervisor assigns. A separate configurable vsock port (the
agent.log_vport option) is reserved for log forwarding; it defaults to 0 and
must be set explicitly.
The agent exposes the AgentService ttrpc service defined in
src/libs/protocols/protos/agent.proto (package grpc). In the current main
branch it has 43 RPC methods. The categories span sandbox lifecycle
(CreateSandbox, DestroySandbox), container lifecycle (CreateContainer,
StartContainer, StatsContainer, and four more), process operations
(ExecProcess, SignalProcess, WaitProcess), I/O, networking
(UpdateInterface, UpdateRoutes, GetIPTables, SetIPTables, and three
more), storage (GetVolumeStats, ResizeVolume, AddSwap), guest
introspection (GetGuestDetails, GetMetrics, GetOOMEvent), hotplug
(OnlineCPUMem, MemHotplugByProbe), and policy.
The breadth reflects Kata's broader scope relative to firecracker-containerd: interface updates, routing changes, iptables management, and memory hotplug are all in-VM operations the shim must be able to drive without touching the host kernel.
Before vsock, Kata used a virtio serial port for agent communication and a
separate kata-proxy process on the host for mux/demux. Vsock eliminated the
proxy. The vhost_vsock kernel module must be loaded on the host
(CONFIG_VHOST_VSOCK=y; sudo modprobe -i vhost_vsock); the kernel
requirement is Linux 4.8 or later.
Before running the above command: loading
vhost_vsockis a host-kernel operation requiring root. Verify the module is available withmodinfo vhost_vsockbefore attempting to load it. On shared hosts or cloud VMs without nested virtualization, the module may not be present.
OCI Bundle Delivery Over the Wire
With runC, delivering a container's rootfs to the container runtime is a local
filesystem operation: the shim mounts the snapshotter's device into the bundle's
rootfs/ directory before calling runc create. With Kata, there is a hardware
boundary between the shim and the container, so the bundle cannot be mounted
locally and then used. Kata handles this in two steps.
The OCI config.json is transmitted to kata-agent via ttrpc — specifically a
CreateContainerRequest over vsock — rather than via a filesystem path inside
the VM. The container rootfs travels by one of two mechanisms depending on
configuration. The default is virtiofs: the shim starts a virtiofsd daemon
on the host (one per VM), exports the snapshotter directory, and the agent mounts
the exported directory as the container rootfs inside the VM. The alternative is
virtio-scsi: when block-based graph drivers are configured, the block device
is hot-plugged into the VM as a virtio-scsi device, appearing as /dev/sda or a
similar SCSI path. A distinct case applies when using the devmapper snapshotter
with Kata-on-Firecracker: there the block device is passed as a virtio-blk
drive, visible inside the VM as /dev/vda.
The guest OS mini-image — the kernel and minimal userspace that runs inside each
Kata VM — is mounted via DAX to avoid double-caching its pages in both the host
and guest page caches. QEMU uses an NVDIMM device backed by a file (the guest
sees it as /dev/pmem*); Cloud Hypervisor uses an emulated Persistent Memory
(PMEM) device; both mount as ext4 inside the VM. virtio-9p is a supported but
non-default alternative to virtiofs for rootfs sharing.
sequenceDiagram
participant K as kubelet
participant C as "containerd CRI"
participant S as "containerd-shim-kata-v2"
participant V as virtiofsd
participant A as kata-agent
participant R as "runc (inside VM)"
K->>C: RunPodSandbox
C->>S: Create (TaskService ttrpc)
S->>S: boot hypervisor VM
S->>V: start virtiofsd (one per VM)
S->>A: CreateSandbox (AgentService vsock:1024)
K->>C: CreateContainer
C->>S: Create (container task)
S->>A: CreateContainer (config.json over vsock)
A->>V: mount virtiofs export as rootfs
A->>R: exec container process
R-->>A: process running
A-->>S: container started
S-->>C: task running
C-->>K: container ready
Networking: TC Redirect Without MACVTAP
Hypervisors cannot use a veth interface directly: veth works at the network namespace boundary in the kernel, but the VM's network interface must attach at the TAP layer so the VMM can forward raw Ethernet frames into the guest. Kata's shim creates a TAP interface in the host network namespace and installs TC redirect rules to bridge traffic bidirectionally between the pod's veth interface (which CNI already populated with an IP and routes) and the VM's TAP interface, at the ingress and egress filter level. This replaced an earlier approach that used MACVTAP; the TC redirect approach is more composable with CNI chains and avoids the MAC address management burden MACVTAP introduces.
The flow across a node where kubelet has assigned the pod network via CNI is:
CNI populates the veth in the pod's network namespace; the shim bridges traffic
from that veth to the TAP; Firecracker or QEMU connects the TAP to a virtio-net
device; the guest kernel sees a eth0 with the CNI-assigned IP. From the pod's
perspective — and from kubelet's — the network is indistinguishable from a plain
container's. From the kernel's perspective, every packet crosses the hardware
boundary of the VM twice.
Four Hypervisors, One Shim
All four VMM backends are invoked through containerd-shim-kata-v2; the
selection is made in configuration.toml. The current releases in the main
branch are QEMU v10.2.1 (supporting x86_64, aarch64, ppc64le, s390x, and
riscv, with the largest device footprint and broadest architecture coverage),
Cloud Hypervisor v51.1 (x86_64 and aarch64, with fine-grained per-thread
seccomp and an HTTP OpenAPI management interface), Firecracker v1.12.1
(x86_64 and aarch64, with the most restrictions: no virtiofs, no device
hotplug, no VFIO, no CPU or memory hotplug, devmapper snapshotter required),
and Dragonball (no separate released version; runs in-process with the
runtime-rs shim at zero IPC overhead).
Firecracker as a Kata backend is configured via
/etc/kata-containers/configuration-fc.toml. Because Firecracker does not
support virtiofs, the devmapper snapshotter is mandatory and virtio-block is the
only rootfs transport. This makes the Kata-on-Firecracker path more constrained
than the Kata-on-QEMU path but also closer to what firecracker-containerd does
directly.
Kata 3.0 introduced runtime-rs, a Rust-based shim with Dragonball as an
embedded VMM running in the same process as the shim. Since the VMM and the shim
share an address space, the REST API round-trip between them disappears entirely.
Kata 4.0 — planned for July 2026 — makes runtime-rs the default; the Go
runtime enters deprecation at that point, receiving only bug and security fixes
with removal no earlier than version 5.0.
Flintlock: The VM As A Kubernetes Node
A Different Level Of Abstraction
firecracker-containerd and Kata Containers both put the microVM inside the container model: the container is the unit of work, and the VM is the isolation mechanism. Flintlock inverts the relationship. The microVM is the unit of infrastructure. Its primary use case is not running application containers but provisioning the nodes those containers will run on — Kubernetes worker and control-plane nodes, managed through an API that speaks OCI image references for kernel and disk images.
Flintlock (liquidmetal-dev/flintlock, formerly weaveworks/flintlock, MPL-2.0)
is a host-level daemon written in Go. It exposes a gRPC service named MicroVM
in package microvm.services.api.v1alpha1 with exactly five RPC methods:
CreateMicroVM, DeleteMicroVM, GetMicroVM, ListMicroVMs, and
ListMicroVMsStream. The gRPC endpoint defaults to localhost:9090; a
grpc-gateway HTTP endpoint defaults to localhost:8090, with REST mappings
for the first four methods (POST /v1alpha1/microvm, DELETE /v1alpha1/microvm/{uid},
GET /v1alpha1/microvm/{uid}, GET /v1alpha1/microvm/{namespace}).
MicroVMSpec: OCI Images As Disks
The MicroVMSpec protobuf message (package flintlock.types, proto3) captures
the full declaration of a microVM:
string id = 1
string namespace = 2
map<string, string> labels = 3
int32 vcpu = 4
int32 memory_in_mb = 5
Kernel kernel = 6
optional Initrd initrd = 7
Volume root_volume = 8
repeated Volume additional_volumes = 9
repeated NetworkInterface interfaces = 10
map<string, string> metadata = 11
google.protobuf.Timestamp created_at = 12
google.protobuf.Timestamp updated_at = 13
google.protobuf.Timestamp deleted_at = 14
optional string uid = 15
The Kernel message (selected fields) carries string image = 1,
map<string,string> cmdline = 2, optional string filename = 3 (to name the
kernel file within the image), and bool add_network_config = 4: the kernel is
an OCI image reference, not a local path. Volume.VolumeSource
has optional string container_source = 1, meaning disk images are also
specified as OCI image references, pulled through containerd. The
NetworkInterface.IfaceType enum has two values: MACVTAP = 0 (required for
Cloud Hypervisor) and TAP = 1 (the only option Firecracker supports).
MicroVMStatus.MicroVMState tracks PENDING, CREATED, FAILED, and
DELETING.
Containerd As The Storage Layer
Flintlock uses containerd as its storage and pull infrastructure, connecting to
the local containerd socket at /run/containerd/containerd.sock. It operates in
the "flintlock" namespace — separate from the "default" namespace that other
tools use — with volumes backed by the devmapper snapshotter
(ContainerdVolumeSnapshotter = "devmapper") and kernel and initrd images
pulled using the native snapshotter (ContainerdKernelSnapshotter = "native").
State is rooted at /var/lib/flintlock; configuration lives in
/etc/opt/flintlockd. The primary VMM backend is Firecracker via
firecracker-microvm/firecracker-go-sdk v1.0.0; Cloud Hypervisor is
experimentally supported, selected at startup with the --default-provider flag.
The pattern of block devices from devmapper snapshots being passed directly to Firecracker as virtio-blk drives is the same as in firecracker-containerd, but the purpose differs: firecracker-containerd passes a container's OCI layer as a block device to run an application inside the VM; flintlock passes an OS disk image as a block device to run a Kubernetes node on top of the VM.
The default resource limits, from github.com/weaveworks/flintlock/pkg/defaults:
two vCPUs, 1024 MB of memory, a ten-second timeout on VM deletion, a ten-minute
resync period, and a maximum of ten retries. The Firecracker binary is resolved
from $PATH with FirecrackerDetach = true, meaning the VMM process is
detached from the daemon's process group.
Cluster API Integration
The operational interface for flintlock at scale is
liquidmetal-dev/cluster-api-provider-microvm (CAPMVM), a Cluster API
Infrastructure Provider registered with clusterctl as InfrastructureProvider
named "microvm". Installation is clusterctl init -i microvm; CNI integration
(for example, Cilium) requires the feature flag EXP_CLUSTER_RESOURCE_SET=true.
CAPMVM defines two core types. MicrovmMachineSpec embeds microvm.VMSpec
inline and adds SSHPublicKeys []microvm.SSHPublicKey and
ProviderID *string. MicrovmMachineStatus carries Ready bool,
VMState *microvm.VMState, Addresses []clusterv1.MachineAddress,
FailureReason, FailureMessage, and Conditions — the standard CAPI machine
infrastructure-provider contract, with GetConditions/SetConditions and a
MachineFinalizer. MicrovmClusterSpec fields include
ControlPlaneEndpoint clusterv1.APIEndpoint, SSHPublicKeys []microvm.SSHPublicKey,
Placement, MicrovmProxy *flclient.Proxy, and TLSSecretRef string.
MicrovmClusterStatus carries Ready bool, Conditions clusterv1.Conditions,
and FailureDomains clusterv1.FailureDomains.
A lighter-weight alternative exists for smaller deployments: the
liquidmetal-dev/microvm-operator, a plain Kubernetes operator (not CAPI) for
creating batches of microVMs on flintlock-managed devices. Its README marks it
as proof of concept.
Where The Two Books Meet
The containerd book traces the container from a containerd.Create call down
through the shim v2 protocol, the snapshotter, the CNI plugin chain, and the TC
networking primitives. This book traces the microVM from a Firecracker API call
down through KVM ioctls, virtio transports, and the hardware isolation boundary.
The junction between the two traces is the containerd shim.
| Topic | Containerd book | MicroVM book extension |
|---|---|---|
| Shim v2 / TaskService | io.containerd.runc.v2 runs OCI containers |
aws.firecracker / io.containerd.kata.v2 substitute a VM for the container sandbox |
| devmapper snapshotter | Container layer storage on the host | Block device passed to Firecracker virtio-blk instead of mounted in the host filesystem |
| TAP + TC redirect | CNI networking for pods | Veth-to-TAP bridge for VM network interfaces in both firecracker-containerd and Kata |
| ttrpc | Containerd-to-shim control plane | Shim-to-in-VM-agent control plane (firecracker-containerd: port 10789; Kata: port 1024) |
The hardware enforcement point is what the table cannot convey on its own. In both microVM paths, EPT or NPT in the CPU enforces the isolation boundary — a kernel vulnerability on the guest side cannot reach the host side without also defeating the hardware virtualization extension. That boundary is absent from plain container isolation, where the same kernel enforces security on both sides of every namespace wall. The shim is the hinge.
Chapters 4 through 8 of this book built the hardware side of that hinge: the
VMCS, the KVM_RUN loop, the virtio queues, and the two-dimensional page
tables. The chapters in the containerd book build the software side: the
snapshotter API, the ttrpc shim protocol, the CNI invocation, and the event
routing.
Sources And Further Reading
- firecracker-containerd architecture and shim design: https://github.com/firecracker-microvm/firecracker-containerd/blob/main/docs/architecture.md and https://github.com/firecracker-microvm/firecracker-containerd/blob/main/docs/shim-design.md
- firecracker-containerd
runtime/service.go(VSOCK constants, drive mounting): https://github.com/firecracker-microvm/firecracker-containerd/blob/main/runtime/service.go - firecracker-containerd snapshotter design: https://github.com/firecracker-microvm/firecracker-containerd/blob/main/docs/snapshotter.md
- firecracker-containerd drive stub pre-allocation: https://github.com/firecracker-microvm/firecracker-containerd/blob/main/docs/design-approaches.md
- firecracker-containerd networking (tc-redirect-tap): https://github.com/firecracker-microvm/firecracker-containerd/blob/main/docs/networking.md
- firecracker-containerd proto types: https://github.com/firecracker-microvm/firecracker-containerd/blob/main/proto/types.proto
- firecracker-containerd agent README: https://github.com/firecracker-microvm/firecracker-containerd/blob/main/agent/README.md
- Firecracker VSOCK multiplexing protocol: https://github.com/firecracker-microvm/firecracker/blob/main/docs/vsock.md
- Firecracker VSOCK internal implementation: https://github.com/firecracker-microvm/firecracker-containerd/blob/main/internal/vm/vsock.go
- containerd runtime v2 protocol: https://github.com/containerd/containerd/blob/main/docs/runtime-v2.md
- containerd TaskService proto: https://github.com/containerd/containerd/blob/main/api/runtime/task/v2/shim.proto
- kata-agent Rust source and README: https://github.com/kata-containers/kata-containers/blob/main/src/agent/README.md
- kata-agent AgentService proto (43 methods): https://github.com/kata-containers/kata-containers/blob/main/src/libs/protocols/protos/agent.proto
- Kata VSOCK design: https://github.com/kata-containers/kata-containers/blob/main/docs/design/VSocks.md
- Kata storage architecture (virtiofs, virtio-scsi, DAX): https://github.com/kata-containers/kata-containers/blob/main/docs/design/architecture/storage.md
- Kata Kubernetes integration (RuntimeClass, sandbox vs. container): https://github.com/kata-containers/kata-containers/blob/main/docs/design/architecture/kubernetes.md
- Kata networking (TC redirect, MACVTAP replacement): https://github.com/kata-containers/kata-containers/blob/main/docs/design/architecture/networking.md
- Kata hypervisor backends and version table: https://github.com/kata-containers/kata-containers/blob/main/docs/design/virtualization.md
- Kata on Firecracker (devmapper requirement, configuration-fc.toml): https://github.com/kata-containers/kata-containers/blob/main/docs/how-to/how-to-use-kata-containers-with-firecracker.md
- Kata 4.0 preview (runtime-rs default, Go runtime deprecation): https://katacontainers.io/blog/release-4-0-0-preview/
- flintlock repository: https://github.com/liquidmetal-dev/flintlock
- flintlock MicroVM gRPC service proto: https://github.com/liquidmetal-dev/flintlock/blob/main/api/services/microvm/v1alpha1/microvms.proto
- flintlock MicroVMSpec proto: https://buf.build/weaveworks-liquidmetal/flintlock/raw/58fe9b39fd874ce7abc0773abe71f072/-/types/microvm.proto
- flintlock default constants: https://pkg.go.dev/github.com/weaveworks/flintlock/pkg/defaults
- flintlock go.mod (SDK versions): https://github.com/liquidmetal-dev/flintlock/blob/main/go.mod
- Cluster API provider for microVMs (CAPMVM): https://github.com/liquidmetal-dev/cluster-api-provider-microvm
- CAPMVM MicrovmMachine types: https://github.com/liquidmetal-dev/cluster-api-provider-microvm/blob/main/api/v1alpha1/microvmmachine_types.go
- CAPMVM MicrovmCluster types: https://github.com/liquidmetal-dev/cluster-api-provider-microvm/blob/main/api/v1alpha1/microvmcluster_types.go