Repository navigation
Firecracker: -fc-balloon returns idle guest memory to the host (#74) - #75
Conversation
A balloon with free page reporting (target 0) returns pages the guest frees, and the guest agent drops caches and compacts once the guest has been quiet for 30 s, since page cache is most of what a busy guest holds. Off by default; without it vm.json and the boot args are unchanged.
Decision: Findings
Tests Run
Raw runtime output was retained in local Gitmoot state and is not posted here. |
Closes #74. Assignment: owner "okay start the work go" (2026-10-22), on phobos-dev's owner-approved request in #74.
What
-fc-balloon(Firecracker driver, off by default) makes running VMs give memory back to the host:vm.jsongetsballoon: {amount_mib: 0, deflate_on_oom: true, stats_polling_interval_s: 0, free_page_reporting: true}. The guest kernel reports freed pages; FirecrackerMADV_DONTNEEDs them. Target 0: the host never takes memory the guest is using.sandboxd.balloon=1). PID 1 samples/proc/statevery 5 s; after 30 s under 5% of one CPU it syncs, writesdrop_caches=3andcompact_memory=1, once per quiet period.Why (2): measured, free page reporting alone returned only ~90 MiB of ~1 GiB. Most of what a busy Phobos guest holds is page cache (omp, the whisper model), which is not free memory, so the balloon never sees it.
Without the flag,
vm.jsonand the boot args are byte-identical to today (TestFirecrackerBalloonoff-case,TestFirecrackerBootArgs).Choices
hardened). Phobos runs its own instance (-fc-instance phobos), so turning the flag on there is per-tenant in practice. The docs say how to scope it: one instance per template set.sandboxd.balloon=1and gets only (1). The Phobos image builder takes its agent from the release tarball, so (2) reaches Phobos after the next release and an image rebuild.Evidence (real KVM, fleet host, scratch instance
b74, live services untouched)Phobos image
phobos-omp-amd64-42437b9ac771with this PR's agent installed into a scratch copy. 1536 MiB guest, 2 vCPUs. Workload = phobos-dev's probe:omp --help, then whisper.cpp small-q8_0 on 60 s of audio, twice, then idle. No manual cache drop.Second round with the flag on: 861 → 263 MiB. Balloon alone (stock v0.4.0 agent): 1138 → 1036 MiB.
Isolation re-probe, flag on, from inside the guest: 1.1.1.1:443 open; 10/8, 192.168/16, 172.16/12, 100.100.100.100, 169.254.169.254, the gateway's ports, and IPv6 are all blocked. The
virtio_balloondriver is bound. The guest user cannot writedrop_caches.Instruments and raw output:
/root/fleet-tools/state/sandboxd-74-balloon/(probe-1..3.txt,isolation-on.txt,run-probe.sh,probe-memory.py,isolation.py).Tests
TestFirecrackerBalloon: the balloon device and boot arg are present exactly when the flag is on, with target 0 and reporting on.TestIdleReclaim: one reclaim per quiet period, the 5% threshold boundary, and a reset on activity.TestParseCPUBusy: guest/guest_nice are not double-counted, idle/iowait excluded, malformed input rejected.test-check prove:inconclusive(old_code_build_failed) for all four. Expected for a new feature: the tests use new symbols. Manual mutants instead, each red:free_page_reporting: false→TestFirecrackerBalloonFAILdoneon activity →TestIdleReclaimFAILTestParseCPUBusyFAIL--pieceslists the flag wiring and the PID 1 goroutine asnot_tested. Both are exercised by the KVM run above, where the flag-on numbers depend on them.go vet ./...,gofmt,go test ./...pass locally.