---
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.