lighter 0.4.0: the guest sized by what it runs, ports as Docker publishes them, IPv6 in containers.
Memory
The guest is sized by what it runs. It boots with a quarter of its configured memory as ordinary RAM and a virtio-mem range for the rest, plugged in as containers need it; a machine running nothing shrinks to its base once the containers have been idle eight seconds, and any container makes the guest whole again before dockerd sees the request. What macOS reports for lighter is now honest: pages the guest hands back leave the footprint for good (a fresh mapping replaces them), and lighter status shows the same physical footprint Activity Monitor does. On an M5 Pro the machine idles at 359 MiB and settles to about 1.15 GB a minute after an npm ci, against OrbStack's 1.2 GB idle and 2.1 GB after; on an 8 GB M1 it idles at 305 MiB against OrbStack's 733.
Idle power
An idle lighter costs 4 ms of CPU per second and 57 wakeups a second on an M5 Pro (OrbStack: 2 and 99), and 8 ms and 58 wakeups on an M1 (0.3.0: 11 and 135). The whole night of it is in docs/worklog.md: the sockmap backlog worker that ticked a vCPU at HZ for any container that stopped reading a published connection (guest kernel patch 0025 and a thirty-second TCP_USER_TIMEOUT), containerd's CRI monitor and garbage collector, the per-CPU statistics fold, kcompactd, a CAN bus timer the defconfig had built in, and the share server's settler thread polling an empty map.
Published ports, the way Docker publishes them
-p 8080:80 binds every interface of the Mac in both families, so another machine on your network reaches the container; -p 127.0.0.1:8080:80 stays on loopback; lighter config --publish localhost keeps every publish on loopback on a Mac that should not offer its containers to the network it is on. UDP publications are carried too, as flows on a stream into the guest, mirroring how a container's own UDP leaves.
IPv6 in containers
Every container has an IPv6 address and default route, and reaches v6 destinations over TCP, UDP and ICMP exactly when your Mac can. On a network without IPv6, names resolve to IPv4 only, so nothing waits on an address that cannot be reached. A container that names its own resolver (--dns 8.8.8.8) now works, in either family; the UDP divert had exempted port 53 since the streams began.
Small machines
The range's blocks are onlined by the kernel's auto-movable policy at a ratio of one (guest patch 0026 makes it the kernel's default): a block is movable only while movable memory is at most equal to the kernel-usable memory, and ordinary memory otherwise, so slab always has room. On an 8 GB M1's 4 GiB guest, where the whole range movable had the kernel reclaiming the tree's dentries and inodes on every pass (cp -a of a package tree on the share 20 s against 5, a second ripgrep pass 2.1 s against 185 ms), the tree cases read as they did before the range, with the range's idle savings kept. One policy at every size.
A container's own firewall
The guest kernel now carries nf_tables' whole expression set (ct state, log, limit, reject, quota, numgen, hash, connlimit, queue, conntrack marks, zones, labels and timeouts). A sandbox loading its firewall with nft -f inside a privileged container failed at the first ct state; Docker's own rules never asked, since they go through the iptables-nft compat path.
Also
- Dual-licensed under MIT or Apache 2.0, at your option.
- One kernel ships. The 1000 Hz twin and its opt-in are gone: what it gave container starts it took from the share's installs.
- The daemon publishes its identity by audit token under its home lock, so
stopcan never signal a recycled pid, and a machine started by 0.3.0 is still found and stopped by its pid file. -p [::]:PORT:...reaches an IPv6-only service in the container; Docker API requests carry one absolute deadline.- Four fixes from a review: the pid file a launchd-started machine never wrote (so
statusandstopcould not see it), a malformed block request that could panic the VMM, unaligned reads and writes of guest memory, and Docker API requests without a read timeout. - The full gate set gained
m3-publish(loopback, LAN and IPv6 listeners, loopback-only publishes, withdrawal, UDP echoes with two hundred clients) and IPv6 checks inm3-streams; all twelve gates pass on the release commit on an M5 Pro.
Numbers, method and the other runtimes' rows on the same machines: the README's Benchmarks section and benchmarks/RESULTS.md.