Memory ballooning doesn’t reduce host RAM usage because the guest only releases memory it explicitly frees. Page cache sits there until the guest decides to drop it, and the balloon can’t force that. You need free-page-reporting and a cache drop to see the host number move.

What ballooning actually does
The virtio-balloon driver inflates inside the guest, which puts pressure on the guest’s memory allocator. The guest then reclaims what it can — but page cache is reclaimable, so the kernel just keeps it. The host sees the same RSS because the guest hasn’t actually returned those pages.
Check it yourself. Run this in the guest:
grep -E 'MemTotal|MemFree|MemAvailable|Cached' /proc/meminfo
Then look at the host: qm status <vmid> --verbose | grep mem. The balloon may show as inflated, but the host’s used memory barely drops. That’s the gap.

The fix: free-page-reporting
Add this to the VM config:
qm set <vmid> -balloon 1 -free_page_reporting 1
Then inside the guest, make sure the balloon driver is loaded and the feature is on:
modprobe virtio_balloon
cat /sys/devices/virtio-pci/virtio*/free_page_reporting
If that says 1, the guest can now report free pages back to the host. But it still won’t drop cache on its own.
Drop caches to actually reclaim
Run this in the guest:
echo 3 > /proc/sys/vm/drop_caches
Now check the host again. The used memory should drop by roughly the amount of cache you had. That’s the missing piece — ballooning alone won’t do it.
For a permanent setup, you can add a cron job in the guest to drop caches periodically, or use a systemd timer. I’d keep it simple: a cron entry every hour is fine for a homelab.
If you’re still seeing high host usage after this, check for other memory hogs in the guest. And if the VM won’t boot after messing with balloon settings, you might need the GRUB fix.