libkrunfw kernels have CONFIG_SWAP=n and CONFIG_ZRAM=n, with CONFIG_MODULES=n, so a guest whose live working set exceeds its krun.ram_mib ceiling can only OOM-kill — even when the host has plenty of memory. There is no other swap path: no block devices are exposed through podman+krun, and virtiofs cannot host swapfiles. zram is the only possible in-guest backend and the config forbids it.
Context: config-libkrunfw_x86_64 had CONFIG_SWAP=y until it was dropped in b085fa0 ("Drop swap and unused memory features", 2025-01). The sev/tdx and aarch64/riscv64 variants still carry SWAP=y (plus ZSWAP/ZSMALLOC on the non-x86 ones), so this is restore-to-parity rather than new surface. The same slimming pass also dropped CONFIG_VM_EVENT_COUNTERS.
Suggested lines for config-libkrunfw_x86_64 (the zstd selectors materialise ZSMALLOC=y and CONFIG_ZRAM_DEF_COMP="zstd" via make olddefconfig; ZSTD_COMPRESS/ZSTD_DECOMPRESS are already =y):
CONFIG_SWAP=y
CONFIG_ZRAM=y
CONFIG_ZRAM_BACKEND_ZSTD=y
CONFIG_ZRAM_DEF_COMP_ZSTD=y
CONFIG_LRU_GEN=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_PSI=y
All of these are defaults on mainstream distro kernels, and all are inert until guest userspace opts in: built-in zram creates exactly one zram0 with disksize 0 and no swap exists until mkswap/swapon; MGLRU is strictly-better reclaim; VM_EVENT_COUNTERS/PSI are telemetry-only.
Size cost: in our rebuild the resulting libkrunfw.so came out the same size as the stock artifact (21,432,000 B) — the added code fits entirely within the kernel image's existing inter-segment alignment padding, so the shipped library doesn't grow. Content-wise the kernel image is ~1% larger (~68 KiB xz-compressed), i.e. a few hundred KiB of resident kernel text; and zram allocates nothing until guest userspace initialises the device (disksize starts at 0).
Motivating cases, both field-observed on 4 GiB podman+krun VMs:
- Three node processes resume heavy work simultaneously at boot — guest OOM at t=16s with ~3.8 GiB nearly-all-inactive anon, zero file cache, and the host idle.
- With swap enabled, the same resume OOM'd via the classic LRU declaring
all_unreclaimable with 3.4 GiB of swap still free — CONFIG_LRU_GEN (MGLRU) fixed it.
End result with all of the above: 11 node sessions + dev servers on a 4 GiB ceiling, 2 GiB compressed at ~4:1 in zram, zero OOMs across resume bursts and repeated restarts.
Happy to send a PR against the configs if the maintainers are open to it.
libkrunfw kernels have
CONFIG_SWAP=nandCONFIG_ZRAM=n, withCONFIG_MODULES=n, so a guest whose live working set exceeds itskrun.ram_mibceiling can only OOM-kill — even when the host has plenty of memory. There is no other swap path: no block devices are exposed through podman+krun, and virtiofs cannot host swapfiles. zram is the only possible in-guest backend and the config forbids it.Context:
config-libkrunfw_x86_64hadCONFIG_SWAP=yuntil it was dropped in b085fa0 ("Drop swap and unused memory features", 2025-01). The sev/tdx and aarch64/riscv64 variants still carrySWAP=y(plus ZSWAP/ZSMALLOC on the non-x86 ones), so this is restore-to-parity rather than new surface. The same slimming pass also droppedCONFIG_VM_EVENT_COUNTERS.Suggested lines for
config-libkrunfw_x86_64(the zstd selectors materialiseZSMALLOC=yandCONFIG_ZRAM_DEF_COMP="zstd"viamake olddefconfig;ZSTD_COMPRESS/ZSTD_DECOMPRESSare already=y):All of these are defaults on mainstream distro kernels, and all are inert until guest userspace opts in: built-in zram creates exactly one
zram0with disksize 0 and no swap exists untilmkswap/swapon; MGLRU is strictly-better reclaim;VM_EVENT_COUNTERS/PSIare telemetry-only.Size cost: in our rebuild the resulting
libkrunfw.socame out the same size as the stock artifact (21,432,000 B) — the added code fits entirely within the kernel image's existing inter-segment alignment padding, so the shipped library doesn't grow. Content-wise the kernel image is ~1% larger (~68 KiB xz-compressed), i.e. a few hundred KiB of resident kernel text; and zram allocates nothing until guest userspace initialises the device (disksizestarts at 0).Motivating cases, both field-observed on 4 GiB podman+krun VMs:
all_unreclaimablewith 3.4 GiB of swap still free —CONFIG_LRU_GEN(MGLRU) fixed it.End result with all of the above: 11 node sessions + dev servers on a 4 GiB ceiling, 2 GiB compressed at ~4:1 in zram, zero OOMs across resume bursts and repeated restarts.
Happy to send a PR against the configs if the maintainers are open to it.