Memory Ballooning and KSM on KVM/Proxmox: Optimize RAM to Run More Virtual Machines

Virtualization tutorial - IT technology blog
Virtualization tutorial - IT technology blog

Same 32GB RAM server — some people run 10–15 VMs on it, while I could only manage 5–6 before the system started crawling. The reason? Usually two overlooked features: Memory Ballooning and KSM (Kernel Same-page Merging).

I run a homelab with Proxmox VE managing 12 VMs and containers — a playground to test everything before pushing to production. Initially I allocated RAM the traditional way: each VM got a hard 2GB or 4GB and that was it. The result was a 32GB server that always reported 28–30GB in use even though most VMs were… sleeping. After enabling KSM and properly configuring the balloon driver, that number dropped to 18–20GB during off-peak hours — nearly 10GB saved through software configuration alone.

The Problem with Rigid RAM Allocation

Most tutorials teach you to create VMs with fixed RAM — say 4GB. That means the hypervisor always reserves 4GB of physical memory for that VM, even when it’s idle and only actually using 800MB.

I noticed this after running free -h on each VM at 3 AM — most were only using 20–30% of their allocated RAM. Pure waste.

Three approaches to fix this:

  • Low fixed RAM allocation: Less waste, but VMs starve under high load → crashes or heavy swapping
  • Memory Ballooning: Dynamic allocation — VMs grab more when needed, return it when idle
  • KSM: The kernel merges identical RAM pages across VMs → physical memory savings

These two aren’t mutually exclusive. Using both together is what real optimization looks like.

How Memory Ballooning Works

Think of it this way: the hypervisor installs a special driver inside the VM called a balloon driver. When the host needs to reclaim RAM, it instructs the balloon driver to inflate — the driver claims RAM inside the guest so the host can give it to another VM. When the VM needs more, the balloon deflates.

The biggest advantage: VMs don’t get killed, they don’t crash. They just slow down slightly while the balloon adjusts — usually a few seconds, barely noticeable for light workloads.

But there are downsides:

  • The guest OS must have the balloon driver installed (VirtIO balloon on Linux, virtio-win on Windows)
  • If the balloon takes too much RAM, the guest starts swapping — I once caused an Ubuntu VM to swap continuously by setting min_balloon too low at 256MB
  • Not suitable for applications requiring low RAM latency (production databases, Redis cache)

KSM — Kernel Same-page Merging

KSM is a Linux kernel feature, independent of the guest OS — enable it once on the host and all VMs benefit. The mechanism: the kernel periodically scans RAM, finds memory pages with identical content (even if they belong to different processes or VMs), then merges them into a single physical page using copy-on-write.

Most effective when running multiple VMs with the same OS — for example, 8 Ubuntu 22.04 VMs all sharing the same kernel, libc, and systemd in RAM. KSM detects this and merges them, saving gigabytes.

After enabling KSM on the Proxmox host, here’s how to check how much you’re saving:

# Check current KSM status
cat /sys/kernel/mm/ksm/pages_shared
cat /sys/kernel/mm/ksm/pages_sharing

# pages_shared: actual physical pages (after merging)
# pages_sharing: pages being shared (savings = sharing - shared)

On my homelab, pages_sharing hovers between 180,000–250,000 pages — equivalent to 700MB–1GB of RAM saved, just from enabling a built-in kernel feature.

Comparison: Which One Should You Use?

Criteria Memory Ballooning KSM
Requires guest changes? Yes (install driver) No
CPU overhead Low Moderate (periodic RAM scanning)
Effectiveness High when VMs are idle High when many VMs share the same OS
Risk Guest swapping if misconfigured CPU spikes during heavy merging
Best for Homelab, dev/test Any environment with many VMs

The practical answer: always enable KSM — there’s no reason not to. Memory Ballooning is more selective: dev/test/CI VMs can balloon freely, but VMs running databases or production Redis should get fixed RAM, no ballooning.

Configuring KSM on Proxmox / KVM Host

KSM is built into the kernel — you just need to enable it and tune a few parameters:

# Enable KSM
echo 1 > /sys/kernel/mm/ksm/run

# Increase scan frequency (default 200ms, reduce to 50ms for homelab)
echo 50 > /sys/kernel/mm/ksm/sleep_millisecs

# Pages scanned per pass (default 100, increase for RAM-heavy servers)
echo 300 > /sys/kernel/mm/ksm/pages_to_scan

These changes don’t survive a reboot. For persistence, use a systemd service — cleaner than rc.local:

cat > /etc/systemd/system/ksm-tune.service << 'EOF'
[Unit]
Description=KSM tuning
After=network.target

[Service]
Type=oneshot
ExecStart=/bin/bash -c 'echo 1 > /sys/kernel/mm/ksm/run; echo 50 > /sys/kernel/mm/ksm/sleep_millisecs; echo 300 > /sys/kernel/mm/ksm/pages_to_scan'
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

systemctl enable --now ksm-tune.service

Configuring Memory Ballooning on Proxmox

Proxmox supports the balloon driver through the web interface. In the VM settings under the Memory tab, there are three parameters to care about:

  • Memory (MiB): Maximum RAM the VM can use
  • Minimum memory: Floor RAM — the host cannot reclaim below this threshold
  • Ballooning Device: checkbox to enable/disable

Or use qm from the command line for faster configuration:

# View RAM configuration for VM 101
qm config 101 | grep -E 'memory|balloon'

# Set max 4GB, min 512MB, enable balloon
qm set 101 --memory 4096 --balloon 512

# Disable balloon (fixed RAM allocation)
qm set 101 --balloon 0

After enabling it, verify the balloon driver is running inside the guest Linux VM:

# Inside the guest VM
lsmod | grep balloon
# Expected output: virtio_balloon ...

# If not loaded, load it manually
modprobe virtio_balloon

# Auto-load on boot
echo 'virtio_balloon' >> /etc/modules

Monitoring — Know How Much You’re Actually Saving

A one-liner script to watch KSM stats in real time on the Proxmox host:

watch -n 5 'echo "=== KSM Stats ==="; \
  echo "Running: $(cat /sys/kernel/mm/ksm/run)"; \
  shared=$(cat /sys/kernel/mm/ksm/pages_shared); \
  sharing=$(cat /sys/kernel/mm/ksm/pages_sharing); \
  saved=$(( (sharing - shared) * 4 / 1024 )); \
  echo "Pages shared: $shared | Pages sharing: $sharing"; \
  echo "RAM saved: ~${saved} MB"'

Don’t worry if the numbers are small at first. KSM needs 20–30 minutes after all VMs boot to finish scanning RAM and complete merging. I typically see stable savings of 800MB–1.2GB after about half an hour.

Common Mistakes and How to Avoid Them

1. VM gets OOM-killed despite balloon headroom: Happens when minimum memory is set too low and the host is under heavy pressure. Set the minimum to at least 40–50% of max memory for production VMs — for a 4GB VM, the minimum should be 1.5–2GB.

2. KSM consuming abnormal CPU: Usually occurs when pages_to_scan is too high while VMs are write-heavy (databases). KSM keeps merging and unmerging due to copy-on-write. Increase sleep_millisecs to 100–200ms to fix it.

3. Windows VM not ballooning: You must install VirtIO drivers from the Proxmox ISO (virtio-win). Open Device Manager in the guest, find the device with no driver, and install VirtIO Balloon from the ISO.

# Download VirtIO drivers ISO for Windows guests
wget https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso
# Place it in /var/lib/vz/template/iso/ on Proxmox

Real-World Results

Before configuring, 12 VMs consistently consumed 26GB out of 32GB of RAM. After enabling KSM and ballooning (8 dev/test VMs ballooned down to a 512MB minimum, 4 production VMs kept at fixed allocation), idle usage dropped to 17–18GB. That freed up nearly 9GB — enough to spin up 3–4 more test VMs without any concern.

More importantly: not a single VM crashed or took a meaningful performance hit. The key is clearly distinguishing which VMs should balloon and which shouldn’t — don’t apply it indiscriminately to everything, especially workloads with heavy I/O.

Share: