--- title: "How to Overcommit Memory Safely on a KVM Host" description: "Run more KVM guests than your RAM allows by using virtio ballooning, KSM page sharing and zswap on Ubuntu 24.04, and measure memory pressure before it hurts." category: "Virtualization VPS" language: en source: https://cubepath.com/docs/virtualization-vps/memory-overcommit-in-virtualization updated: 2026-09-25 --- # How to Overcommit Memory Safely on a KVM Host Memory overcommit means assigning more RAM to virtual machines than the host physically has. It works because guests rarely use all of their memory at the same time, and because identical pages (the same kernel, the same libraries) can be stored once. Done carelessly, it ends with the host swapping heavily or the kernel's OOM killer terminating a guest. In this tutorial you will measure your current allocation, enable the three mechanisms KVM offers (virtio ballooning, Kernel Samepage Merging and compressed swap), and set up the signals that tell you when you have gone too far, on an Ubuntu 24.04 host with libvirt. ## Prerequisites To follow this guide you need: - A physical or bare metal server running Ubuntu 24.04 LTS with KVM and libvirt installed (`qemu-kvm`, `libvirt-daemon-system`, `libvirt-clients`). - A non-root user with `sudo` privileges. - One or more running Linux guests. This guide uses one called `vm1` with 4 GiB of RAM. ## Step 1 - Measuring host memory and current allocation Start with what the host has: ```bash free -h ``` ```output total used free shared buff/cache available Mem: 62Gi 41Gi 1.2Gi 12Mi 20Gi 20Gi Swap: 8.0Gi 0.0Ki 8.0Gi ``` The `available` column is the real headroom: free memory plus cache the kernel can reclaim. Now add up the maximum memory assigned to all running guests. `virsh dominfo` reports it in KiB: ```bash for vm in $(sudo virsh list --name); do sudo virsh dominfo "$vm" | awk -v vm="$vm" '/^Max memory/ {printf "%-20s %6.1f GiB\n", vm, $3/1048576}' done ``` ```output vm1 4.0 GiB web01 16.0 GiB db01 32.0 GiB build01 16.0 GiB ``` In this example, 68 GiB is assigned on a 62 GiB host, an overcommit ratio of about 1.1:1. Keep this number in mind; the rest of the guide is about raising it without hurting guests. | Ratio | Typical use | |---|---| | Up to 1.2:1 | Production guests, databases. Ballooning and KSM absorb the difference. | | 1.2:1 to 1.5:1 | Mixed workloads with many similar guests. Requires monitoring from Step 5. | | Above 1.5:1 | Test, CI and desktop environments where occasional slowdowns are acceptable. | ## Step 2 - Using the virtio balloon to reclaim guest memory A balloon driver inside the guest allocates memory on request from the host and hands it back, so the host can reclaim RAM from an idle guest without stopping it. Guests created with `virt-install` include a balloon device by default. Check: ```bash sudo virsh dumpxml vm1 | grep -A2 memballoon ``` ```output
``` If it shows `model='none'` or no block at all, add the device with `sudo virsh edit vm1`, placing it inside ``: ```xml ``` `` makes the guest report its memory usage every 10 seconds. The change takes effect after a full shutdown and start. Linux guests load the `virtio_balloon` driver automatically. To enable statistics on a running guest without restarting: ```bash sudo virsh dommemstat vm1 --period 10 --live ``` Then read them: ```bash sudo virsh dommemstat vm1 ``` ```output actual 4194304 swap_in 0 swap_out 0 major_fault 812 minor_fault 2211904 unused 2764112 available 4020308 usable 3102460 last_update 1758792345 rss 1603820 ``` `actual` is the memory currently given to the guest, `unused` is what the guest is not using at all, and `rss` is what the guest really occupies on the host. Here the guest has 4 GiB but only needs about 1.5 GiB. Shrink the guest's memory while it runs. The value must be at or below the maximum memory: ```bash sudo virsh setmem vm1 2G --live ``` Check the result from inside the guest with `free -h`: the total drops to about 2 GiB. To give the memory back, run `sudo virsh setmem vm1 4G --live`. To let a guest grow above its boot size later, its maximum (``) must be larger than its current size (``). Set the maximum while the guest is off: ```bash sudo virsh shutdown vm1 sudo virsh setmaxmem vm1 8G --config sudo virsh setmem vm1 4G --config sudo virsh start vm1 ``` The guest boots with 4 GiB and can be ballooned up to 8 GiB. > Note: Ballooning is manual in libvirt: nothing resizes guests for you. Deflate idle guests during quiet periods, and never balloon a guest below what its workload needs, or it will start swapping inside. ## Step 3 - Enabling Kernel Samepage Merging KSM scans the memory of processes that opt in (QEMU does so for all guest RAM), finds identical pages, and merges them into a single copy-on-write page. Ten Ubuntu guests running the same kernel share a large amount of memory. Check whether KSM is running: ```bash cat /sys/kernel/mm/ksm/run ``` `0` means stopped. Start it: ```bash echo 1 | sudo tee /sys/kernel/mm/ksm/run ``` The scanner rate is controlled by two values. The defaults are conservative; raising `pages_to_scan` merges faster at the cost of some host CPU: ```bash echo 1000 | sudo tee /sys/kernel/mm/ksm/pages_to_scan echo 20 | sudo tee /sys/kernel/mm/ksm/sleep_millisecs ``` These settings are lost at reboot. Make them persistent with a `tmpfiles.d` entry, which systemd applies at boot: ```bash sudo nano /etc/tmpfiles.d/ksm.conf ``` ```text w /sys/kernel/mm/ksm/pages_to_scan - - - - 1000 w /sys/kernel/mm/ksm/sleep_millisecs - - - - 20 w /sys/kernel/mm/ksm/run - - - - 1 ``` Apply it now to confirm the file is valid: ```bash sudo systemd-tmpfiles --create /etc/tmpfiles.d/ksm.conf ``` Give KSM a few minutes, then check how much it saves: ```bash grep -H . /sys/kernel/mm/ksm/pages_shared /sys/kernel/mm/ksm/pages_sharing /sys/kernel/mm/ksm/general_profit ``` ```output /sys/kernel/mm/ksm/pages_shared:182340 /sys/kernel/mm/ksm/pages_sharing:1204518 /sys/kernel/mm/ksm/general_profit:5467201536 ``` `pages_sharing` counts the pages being deduplicated and `general_profit` is the net saving in bytes (about 5.1 GiB here) after subtracting KSM's own metadata. If `pages_sharing` stays low after an hour, your guests are too different for KSM to help and you can turn it off to save CPU. > Warning: Page sharing between guests has been used for side-channel attacks between VMs. Do not enable KSM on hosts that run guests from mutually untrusted tenants. To exclude a single sensitive guest, add `` inside its `` block. ## Step 4 - Adding compressed swap with zswap When overcommit goes wrong, the host must swap. zswap reduces the cost by compressing pages into a RAM pool before anything reaches the disk. It needs a swap device behind it. Check your swap: ```bash swapon --show ``` If nothing is listed, create an 8 GiB swap file: ```bash sudo fallocate -l 8G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab ``` Check whether zswap is enabled on the running kernel: ```bash cat /sys/module/zswap/parameters/enabled ``` If it prints `N`, enable it now and pick a compressor and pool limit: ```bash echo zstd | sudo tee /sys/module/zswap/parameters/compressor echo 20 | sudo tee /sys/module/zswap/parameters/max_pool_percent echo 1 | sudo tee /sys/module/zswap/parameters/enabled ``` `max_pool_percent` caps the compressed pool at 20 % of RAM. To make it permanent, add the same settings to the kernel command line: ```bash sudo nano /etc/default/grub ``` Append the options to the existing `GRUB_CMDLINE_LINUX_DEFAULT` value, keeping anything already there: ```ini GRUB_CMDLINE_LINUX_DEFAULT="zswap.enabled=1 zswap.compressor=zstd zswap.max_pool_percent=20" ``` ```bash sudo update-grub ``` Also lower the host's swappiness so the kernel prefers dropping cache over swapping guest memory: ```bash echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/60-swappiness.conf sudo sysctl --system ``` ## Step 5 - Watching for memory pressure Free memory alone does not tell you whether guests are suffering. The kernel's Pressure Stall Information shows the share of time tasks were stalled waiting for memory: ```bash cat /proc/pressure/memory ``` ```output some avg10=0.00 avg60=0.00 avg300=0.00 total=184224 full avg10=0.00 avg60=0.00 avg300=0.00 total=90312 ``` `some` is the percentage of time at least one task was stalled, `full` the percentage when all were. Values near zero are healthy. Sustained `some avg60` above 10 or any sustained `full` value means your overcommit is hurting performance. Check swap activity with `vmstat`, which prints a line every 5 seconds: ```bash vmstat 5 ``` The `si` and `so` columns are pages swapped in and out per second. Occasional swap out is acceptable; constant swap in means guests are waiting on disk. For each guest, `swap_in` and `major_fault` in `virsh dommemstat` show whether the guest itself is short of memory after ballooning. Finally, check whether the OOM killer has already acted: ```bash sudo journalctl -k --since "7 days ago" | grep -i "out of memory" ``` A QEMU process in that output means a guest was killed. Reduce the overcommit ratio or add RAM before relying on that host again. ## Troubleshooting **`virsh setmem` returns `error: invalid argument: cannot set memory higher than max memory`.** Raise the maximum with `setmaxmem ... --config` while the guest is off (Step 2). **`setmem` succeeds but the guest's memory does not change.** The balloon driver is not loaded in the guest. Check with `lsmod | grep virtio_balloon` inside the guest, and confirm the `` device exists. **KSM uses a lot of CPU.** The `ksmd` kernel thread shows in `top`. Reduce `pages_to_scan` or increase `sleep_millisecs`. **Guests are killed by the OOM killer even with swap.** The host ran out of memory and swap together. Lower the overcommit ratio, balloon idle guests down, or protect critical guests by keeping their memory fully backed with huge pages, which are never reclaimed. ## Conclusion You measured your overcommit ratio, learned to reclaim memory from idle guests with the virtio balloon, deduplicated identical pages with KSM, added a compressed swap tier with zswap, and set up the pressure metrics that tell you when to stop. Next, consider exporting `/proc/pressure/memory` and `virsh dommemstat` to your monitoring system, pinning critical guests to dedicated CPUs and NUMA nodes, and reviewing the ratio each time you add guests.