Problem
Hosts run with load_memory = 1.0 (100% of physical RAM sellable) and VMs are created without a balloon value (make_config in lnvps_api/src/host/proxmox.rs never sets it). With balloon unset, Proxmox defaults to balloon target = memory, i.e. no dynamic ballooning — so when host memory pressure spikes there is no graceful reclaim path and the failure mode is the host OOM killer terminating QEMU processes (customer VMs hard-killed).
This has happened in production before: VMs were OOM-killed on a host running 200% memory overcommit.
Proposal
Add a configurable balloon floor so PVE auto-ballooning acts as a pressure-relief valve:
- New setting
QemuConfig.balloon_min_pct: Option<u8> (lnvps_api/src/settings.rs), e.g. 90 = the VM is guaranteed 90% of its sold RAM; auto-balloon may reclaim up to the remaining 10% under host pressure.
- In
make_config: balloon = memory_mb * pct / 100 when configured; unset / 0 / >= 100 leave the field out (current behaviour).
- Existing VMs pick the value up on their next
configure_vm.
Behaviour notes
- PVE auto-balloon only engages when host memory usage exceeds ~80%; below that every VM keeps its full allocation.
- Reclaim is proportional (weighted by
shares, default 1000) and guests surrender reclaimable memory (page cache) first — usually invisible to customers.
- Failure hierarchy becomes: balloon squeeze (graceful) → guest pressure → host OOM-kill, instead of jumping straight to OOM-kill.
- Keep
load_memory = 1.0 — this is spike protection, not a licence for RAM overcommit.
- Requires
virtio_balloon in guests (present in all Linux cloud images).
Interaction with VDS (#195)
Dedicated/VDS templates must be exempt: they should get balloon: 0 (no balloon device) — squeezing "dedicated" RAM contradicts the product.
Acceptance criteria
balloon_min_pct configurable per Proxmox host config; when set, created/reconfigured VMs carry the computed balloon value.
- Unset/0/>=100 produce no
balloon key (unchanged behaviour).
- Unit tests for the floor computation and its propagation through
make_config.
Problem
Hosts run with
load_memory = 1.0(100% of physical RAM sellable) and VMs are created without aballoonvalue (make_configinlnvps_api/src/host/proxmox.rsnever sets it). With balloon unset, Proxmox defaults to balloon target = memory, i.e. no dynamic ballooning — so when host memory pressure spikes there is no graceful reclaim path and the failure mode is the host OOM killer terminating QEMU processes (customer VMs hard-killed).This has happened in production before: VMs were OOM-killed on a host running 200% memory overcommit.
Proposal
Add a configurable balloon floor so PVE auto-ballooning acts as a pressure-relief valve:
QemuConfig.balloon_min_pct: Option<u8>(lnvps_api/src/settings.rs), e.g.90= the VM is guaranteed 90% of its sold RAM; auto-balloon may reclaim up to the remaining 10% under host pressure.make_config:balloon = memory_mb * pct / 100when configured; unset /0/>= 100leave the field out (current behaviour).configure_vm.Behaviour notes
shares, default 1000) and guests surrender reclaimable memory (page cache) first — usually invisible to customers.load_memory = 1.0— this is spike protection, not a licence for RAM overcommit.virtio_balloonin guests (present in all Linux cloud images).Interaction with VDS (#195)
Dedicated/VDS templates must be exempt: they should get
balloon: 0(no balloon device) — squeezing "dedicated" RAM contradicts the product.Acceptance criteria
balloon_min_pctconfigurable per Proxmox host config; when set, created/reconfigured VMs carry the computedballoonvalue.balloonkey (unchanged behaviour).make_config.