Introduction
You SSH into your Proxmox VE host, run free -h out of habit, and the number in the used column makes your stomach drop. 28 GB out of 32 GB, on a node with maybe three modest VMs running. Nothing crashed. Nothing feels slow. But that number says you're almost out of memory, so now you're digging through menus looking for the leak.
Here's the thing: most of the time, there isn't one. Proxmox hosts report memory in a way that looks alarming if you're used to reading it the way you'd read a laptop's Activity Monitor, and on top of that, ZFS and KSM both do their own memory bookkeeping that muddies the picture further. This guide walks through how to actually read those numbers, how to tell real memory pressure from normal caching behavior, and what to do in the cases where the pressure turns out to be genuine.
Commands and menu paths below are current as of Proxmox VE 8.x and 9.x. Nothing here needs anything beyond what ships on a default install.
What You Will Learn
- How to read
free -hcorrectly, including which column actually matters - Why ZFS's ARC cache inflates "used" memory, and how much of that is actually reclaimable
- How KSM quietly reduces VM memory usage in the background, and how to check it's working
- How to find out which specific VM or container is actually responsible for high memory usage
- How to tell a real out-of-memory situation from a host that's just caching aggressively
- What to change — ARC limits, ballooning, or VM sizing — once you've confirmed the pressure is real
What Is This Feature?
There's no single dial called "RAM usage" here — this is really about three separate systems on a Proxmox host that all consume memory in ways the plain used number doesn't explain on its own.
The first is ordinary Linux page caching. Every Linux system, Proxmox included, uses spare RAM to cache recently read files and disk blocks, because RAM is faster than disk and the kernel would rather use idle memory than waste it. This cache gets dropped instantly the moment something else actually needs that RAM. It's not a leak; it's memory doing its job.
The second is ZFS ARC (Adaptive Replacement Cache), which is ZFS's own read cache, and it's the biggest single reason Proxmox nodes look like they're using far more RAM than their VMs are configured for. Unlike ordinary page cache, ARC's usage doesn't show up under buff/cache in free -h — it shows up under used, which is exactly the column that makes people panic. As of Proxmox VE 8.1 (which shipped OpenZFS 2.2), ARC defaults to a ceiling of 10% of system RAM, capped at 16 GiB — a lot more sane than the old 50%-of-RAM default, but still enough to make a 64 GB host show 6-16 GB of "used" memory before a single VM has even started.
The third is KSM (Kernel Samepage Merging), a kernel feature Proxmox enables by default through the ksmtuned service. KSM scans memory pages across all your VMs, and when it finds identical pages — which happens a lot when you're running several VMs of the same OS — it merges them into one shared page instead of storing duplicates. This is a big part of why Proxmox can safely let you overcommit memory across VMs; the actual physical RAM footprint is often smaller than the sum of what you've allocated.
Why Would You Use It?
Understanding this stuff isn't just academic. If you take free -h at face value and start shutting down VMs or buying more RAM every time "used" creeps up, you're solving a problem that doesn't exist and missing the one that might.
It also matters for sizing decisions. If you don't know that ARC alone can eat 16 GB on a well-stocked host, you'll misjudge how much RAM you actually have left for new VMs, and either overcommit dangerously or leave capacity sitting unused out of caution.
And when there is a real problem — an actual memory leak in a service, a VM with a runaway process, genuine swap thrashing — you want to recognize it fast instead of writing it off as "oh, that's just ZFS being ZFS." The two situations look similar in a quick glance at free -h and very different once you know where to look next.
Prerequisites
- SSH or shell access to your Proxmox VE host (the Shell button under Datacenter → Node works fine too)
- At least one VM or container running, so there's something real to check
- Root, or a user account with permission to run
zpool/zfscommands if your storage uses ZFS
Everything below uses tools that already exist on a stock Proxmox VE install — arc_summary ships as part of zfsutils-linux, which every ZFS-based install already has. No extra packages required unless you're specifically told to install one.
Step-by-Step Tutorial
Step 1: Read free -h the right way
Run this first:
free -h
You'll get a table with columns for total, used, free, shared, buff/cache, and available. Most people stare at used and stop there — that's the mistake. The column that actually tells you whether you're in trouble is available: it's the kernel's own estimate of how much memory a new process could get right now, accounting for cache it's willing to drop and pages it can reclaim.
If available is still a healthy chunk of your total RAM, you're fine, regardless of how scary used looks. If available is genuinely low — like, under a gigabyte on a host with real VM workloads — that's when it's worth digging further.
Step 2: Check the node's memory graph in the GUI, but don't stop there
Click your node in the left tree, then Summary. The memory graph here lumps together everything competing for RAM — VM allocations, ARC, KSM savings, host services — into one bar. It's useful for spotting a trend over time (a slow climb toward 100% over days is worth investigating), but it won't tell you what's actually driving the number. For that you need the next few steps.
Step 3: Check how much RAM ZFS's ARC is actually using
If your storage uses ZFS — including the root pool, if you installed with ZFS as the boot filesystem — run:
arc_summary
Look near the top for a line like ARC size (current) and compare it against ARC size (max). If current is sitting right at max, ARC has grown to fill its ceiling, which is completely normal — that's what a cache is supposed to do. It'll shrink automatically the moment something else needs that memory more urgently.
You can also get a quick numeric read straight from the kernel without the full report:
cat /proc/spl/kstat/zfs/arcstats | grep -E "^size|^c_max"
Both numbers are in bytes. If size is close to c_max, you've confirmed ARC as a chunk of your "used" total — and it's memory you can get back any time the host needs it.
Step 4: Check what KSM has already saved you
See how much RAM KSM is currently deduplicating:
cat /sys/kernel/mm/ksm/pages_shared
cat /sys/kernel/mm/ksm/pages_sharing
pages_sharing is the number of pages KSM merged that would otherwise have been separate copies — multiply it by 4096 (the standard page size in bytes) to see roughly how much RAM it's saving you right now. On a host running four or five VMs of the same Linux distro, this number is often surprisingly large.
Confirm the service itself is actually running:
systemctl status ksmtuned
Step 5: Find out which VM or container is actually using the most memory
Go to Datacenter in the GUI and look at the MEM column across your VM and container list — it's sorted per-guest, so outliers stand out fast. For the command-line version:
for id in $(qm list | awk 'NR>1 {print $1}'); do
echo "VM $id: $(qm config $id | grep -E '^memory|^balloon')"
done
This prints each VM's configured memory and balloon settings side by side, which is usually enough to spot a VM someone gave 16 GB to and forgot about.
Step 6: Rule out swap
Cache filling up RAM is normal. Swap filling up is not. Check it with:
free -h
swapon --summary
If the Swap row in free -h shows real, climbing usage — not just a few hundred MB left over from a one-time spike — that's a genuine signal the host doesn't have enough real memory for what you've asked it to run, and no amount of ARC or KSM tuning will fix that on its own.
Step 7: If the pressure is real, fix the actual cause
Once you've ruled out cache and confirmed real pressure, you've usually got three options, and which one fits depends on what step 3 through 6 turned up.
If ARC is the main consumer and you need that RAM back for guests, cap it. Create or edit /etc/modprobe.d/zfs.conf:
options zfs zfs_arc_max=8589934592
That example caps ARC at 8 GiB (the value is in bytes). Apply it and reboot:
update-initramfs -u
reboot
If you can't reboot right now, you can shrink it live, though the effect is less reliable than a proper reboot with the module parameter set:
echo 8589934592 > /sys/module/zfs/parameters/zfs_arc_max
If a specific VM is genuinely oversized for what it does, right-click it, choose Hardware, and lower the memory allocation — or use qm set <VMID> --memory 4096 from the shell. And if ballooning is disabled on a VM that doesn't need a fixed allocation, turning it on with a sensible minimum gets you back some breathing room without touching the max.
Commands Explained
| Command | What it does |
|---|---|
free -h | Shows total, used, free, cached, and swap memory in human-readable units. The available column is the one that actually reflects reclaimable capacity. |
arc_summary | Prints a detailed report of ZFS's ARC cache, including current size, maximum size, and hit/miss rates. |
cat /proc/spl/kstat/zfs/arcstats | Raw kernel statistics for ZFS ARC, in bytes — useful for scripting or a quick check without the full arc_summary report. |
cat /sys/kernel/mm/ksm/pages_sharing | Number of memory pages KSM has currently merged across processes/VMs — multiply by 4096 for bytes saved. |
systemctl status ksmtuned | Confirms the KSM tuning daemon is active and running. |
qm config <VMID> | Prints a VM's full configuration, including its memory and balloon settings. |
qm set <VMID> --memory <MB> | Changes a VM's configured memory allocation. |
swapon --summary | Lists active swap devices and how much of each is currently in use. |
Common Errors
free -h shows 90%+ used but the host feels fine and nothing's crashing. This is almost always ARC and page cache doing exactly what they're supposed to. Check the available column before assuming anything's wrong.
Out of memory: Killed process 1842 (kvm) total-vm:9234184kB
This one in dmesg or /var/log/syslog is the real deal — the kernel's OOM killer stepped in and killed a process, in this case a VM, because it genuinely couldn't find enough memory. This means your host is overcommitted for real, not just showing a scary-looking cache number. Go back to steps 5 and 6 and start trimming actual allocations.
ARC size stays pinned at max even when a VM needs memory urgently. Older ZFS releases were sometimes slow to release ARC under sudden pressure. If you're seeing this consistently rather than as a one-off, setting an explicit zfs_arc_max well below your total RAM gives the kernel more headroom to work with instead of relying on ARC to shrink itself in time.
Troubleshooting
If you've ruled out ARC, KSM, and swap, and memory is still climbing over days without a matching increase in VM workload, the next suspect is a leak in a host-level process rather than a guest. Check with:
ps aux --sort=-%mem | head -15
Look for anything unexpected near the top that isn't a kvm process tied to a VM you know about. Proxmox's own management daemons (pvestatd, pvedaemon, pmxcfs) normally use a modest, stable amount of memory — if one of them is climbing steadily and never settling, restarting the service (systemctl restart pvestatd, for example) is a safe first step, and worth reporting on the Proxmox bug tracker if it recurs.
If memory usage spikes specifically during backup windows, that's expected — vzdump reads through VM disks and that traffic briefly inflates page cache. It should drop back down within a few minutes of the job finishing, not stay elevated.
Worth checking too: a container using an NFS or CIFS mount with heavy write caching can hold onto more page cache than you'd expect from its actual disk usage. That's normal, reclaimable cache — same story as ARC, just from a different source.
Best Practices
Set an explicit zfs_arc_max on any host running more than two or three VMs alongside ZFS, rather than trusting the default ceiling to always leave you enough headroom. Somewhere around 10-15% of total RAM is a reasonable starting point for a mixed VM/storage node — go lower only if you've confirmed your workloads don't lean on ZFS read performance much.
Don't disable KSM to "save CPU" unless you've actually measured it costing you something. On homelab hardware the memory savings from deduplicating a handful of similar VMs usually outweigh the modest background CPU it costs.
Check your baseline. Run free -h and arc_summary on a normal, healthy day and just remember roughly what the numbers look like. That fifteen-second habit is what makes a real problem jump out at you later instead of blending into the usual noise.
Leave ballooning on for VMs where the workload is bursty — a Home Assistant instance or a lightly used Nextcloud VM rarely needs its full allocation held every second. Turn it off only for things that genuinely need guaranteed memory, like databases or anything doing real-time work.
Frequently Asked Questions
Is Proxmox leaking memory if "used" keeps climbing?
Almost never. A slow climb toward your total RAM is usually ARC and page cache filling available space, which is intentional. Check the available column and swap usage before assuming a leak.
Why does free -h show more used memory than all my VMs combined are configured for?
ZFS ARC is the usual reason — it counts as "used," not "cached," and can occupy several gigabytes on its own depending on your zfs_arc_max setting.
Do I need to cap ARC on every Proxmox host?
No. A host with light VM density and plenty of spare RAM can leave it at the default. Cap it once you're running enough VMs that you actually need that headroom back.
Will adding more RAM fix this?
Only if step 5 or 6 above showed genuine pressure — actual swap usage or an OOM kill in the logs. If the "problem" turns out to be ARC and cache, more RAM just gives ZFS more room to cache in, and the used percentage looks the same.
Does KSM slow down my VMs?
Not noticeably for typical homelab use. It costs a bit of background CPU on the host, but the memory savings usually make overcommitting VMs safer, which is the whole point of having it on.
Conclusion
A scary number in free -h is rarely the emergency it looks like at first glance. Most of the time you're looking at ZFS ARC and ordinary page cache doing exactly what caches are supposed to do — and the fix isn't more RAM, it's five minutes with arc_summary and the available column to confirm there's nothing actually wrong. Save the real troubleshooting — swap usage, OOM kills, a leaking process — for when the evidence actually points there.