Guest kernel modules on demand
Guest kernel modules on demand
A template carries only the modules a guest needs to reach its host (vsock, virtiofs). dockerd, Podman, k3s and WireGuard need more: bridge, veth, overlay, nf_tables and so on. The host attaches its guest kernel's whole module tree to every VM as one read-only disk, and the guest loads what a workload asks for. No template carries the tree, and no template needs a rebuild beyond picking up a current in-VM agent.
How it works
- At start, every host builds
/var/platinum/data/templates/_kernel/kmod-<kver>-<sha16>.sqfsfrom its canonical module tree (host-agentguestkmod_disk.go). The build is reproducible and the tree is identical fleet-wide, so every host holds the same file under the same name. It takes about a second on the first start after a kernel change, and the file is 137 MiB per kernel. Images are never deleted, because memory snapshots name them. - With the flag on, a cold boot attaches it
readonly=onwith serialpt-kmod. - The in-VM agent mounts it
ro,nosuid,nodev,noexecat/lib/modules/.pt-kmodand setskernel.modprobeto/usr/local/bin/pt-modprobe, which is the agent itself (invm-agentmodprobe.go). It loads Ubuntu's.ko.zstfiles throughfinit_module(2), so images need neither kmod nor a zstd-capable one. - A resume whose snapshot names an image this host lacks cold-boots instead
(
loadAndResumeLocked).
On by default
Every lane ran with PT_GUEST_KMOD=1 by 2026-10-07 (both dev hosts, the 5 EU
prod hosts, the US host), so the disk is now attached unless
PT_GUEST_KMOD=0. A host builds its image at start, before it takes a guest,
so a new host can no longer come up as the one host whose guests lose Docker.
To switch it off on a host, set PT_GUEST_KMOD=0 in /etc/platinum/host.env
and restart platinum-host-agent. Snapshots taken with the disk keep
restoring, because the image stays on disk.
What the guest sees
- A template built with an agent from before this change ignores the disk and boots exactly as before.
- The agent overlays the host's tree on the template's own
/lib/modules/<kver>. The template's directory is the overlay's upper layer, so its files win and anything written there (depmod, dpkg, DKMS) lands on the root disk as before. An image's ownmodprobe(kmod with zstd, as in ubuntu and debian) then finds every module. The kernel's automatic loading (mount, socket, netlink, iptables) goes throughpt-modprobe, which needs no kmod. Runpt-modprobe NAMEto load one by hand. /etc/modprobe.dis not read bypt-modprobe.- The agent sets
kernel.modprobelast. pt-init (templates built from this change on) waits for it, at most 3 s, before it loadsmodules-load.dand starts the image's entrypoint, so an entrypoint that runsset -e; modprobe br_netfilterin its first millisecond works.
The rest of a normal VM's boot
pt-init runs no systemd or udev. What they do on a normal VM, a guest now gets too (platinum#1444):
| How | Templates | |
|---|---|---|
inotify watches 524288, instances 8192, vm.max_map_count 1048576, kernel.pid_max 4194304 | kernel command line (sysctl.<key>=, host-agent guest_baseline.go) | every cold boot |
| the same, in a guest that booted without them | raised in place after each restore and resume, and at host-agent start; never lowered | running guests |
modules-load.d, then the image's sysctl.d (sysctl --system, or the same files in order for busybox) | pt-init pt-vm-baseline | built from this change on |
binfmt_misc mounted | pt-init | built from this change on |
/dev/fuse 0666 | agent and pt-init; the host does it for a guest whose pt-init predates this | all |
The image's sysctl.d wins over the command-line values, as on any VM, except
kernel.panic: it stays the host's, which reboots a panicked guest into the
boot watchdog.
Security
The VM is the boundary. A sandbox user is already root in the guest, and root
can already load any module. Modules bind only to the virtio devices the VM
has. The disk is read-only on the host and in the guest. An alias never
resolves to a module that Ubuntu's blacklist-rare-network.conf names, so an
unprivileged socket(2) cannot load those protocols. A requested name is never
read as a path.
Cost (Development, 2026-10-06)
| Without | With | |
|---|---|---|
| Create to running (median, same template and host) | 1.87 s | 1.87 s |
| Cloud Hypervisor threads per VM | 14 | 15 |
| Guest slab after boot | 17.8 MiB | 18.6 MiB |
| One module request (median, 2 vCPU guest) | — | 5.9 ms miss, 6.7 ms alias hit |