PSA: mystery memory pressure / "high system CPU" on a KVM VPS? Check for host-side ballooning
-
PSA: mystery memory pressure / "high system CPU" on a KVM VPS? Check for host-side ballooning
Sharing this because it cost me two days of chasing ghosts inside my own server. Nothing below touches Cloudron: it is all read-only checks plus standard Ubuntu monitoring packages.
See this?
- System CPU creeping up over weeks, reset by a reboot
- Memory graphs showing huge usage, but your app containers only account for a fraction of it
- Swap constantly churning, kswapd busy, everything slightly sluggish
free -hsays tens of GB "used" that no process owns
Check this
1. How much memory is unaccounted for? Every byte should belong to a process or a kernel counter. This sums the counters and prints what is missing:
awk '/^MemTotal:|^MemFree:|^Buffers:|^Cached:|^SwapCached:|^AnonPages:|^Slab:|^KernelStack:|^PageTables:|^VmallocUsed:|^Percpu:/ {a[$1]=$2} END {m=a["MemTotal:"]; for (k in a) if (k!="MemTotal:") m-=a[k]; printf "unaccounted: %.1f GB of %.1f GB\n", m/1048576, a["MemTotal:"]/1048576}' /proc/meminfoHealthy is near zero (a GB or two). Mine showed 45GB of 64GB unaccounted at the worst moment: memory the host had taken back via the virtio balloon.
2. Is the balloon driver actively working?
dmesg | grep -i balloon ls -l /sys/bus/virtio/devices/*/driver | grep balloonA virtio_balloon device merely existing is normal on KVM. But if you see lines like this, the host is resizing the balloon so hard the kernel complains:
workqueue: update_balloon_size_func hogged CPU for >10000us 8192 times3. Catch it in the act. The spikes can last only 10-15 minutes, so a spot check misses them. Drop a one-line cron heartbeat (safe, read-only):
echo '* * * * * root awk "/^MemTotal:/{t=\$2} /^MemAvailable:/{a=\$2} END{print strftime(\"%FT%T\"), \"used_pct=\" int(100*(t-a)/t)}" /proc/meminfo >> /var/log/mem-heartbeat.log' > /etc/cron.d/mem-heartbeatA sawtooth like
used_pct=9 ... 23 ... 80 ... 9within minutes, with no matching process activity, is the balloon inflating and deflating. Alsoapt install sysstatand enable it;sar -randsar -ugive you history for the ticket. When a spike hits, capturecat /proc/meminfo,ps aux --sort=-rss | head -25anddocker stats --no-streamin the same minute: processes small + huge unaccounted gap = proof.Do this to solve
You cannot fix this from inside the guest; the balloon is controlled by the hypervisor. In our case (SSD Nodes, 64GB plan) the host was repeatedly reclaiming up to 45GB, and the "high system CPU" was just my kernel's balloon workqueue plus kswapd feeding it.
(SSDnodes have been great otherwise and would definitely use again and recommend them to the community if you need something with lots of RAM for eg an experimental VPS / hobby.)
- Open a ticket with your provider with the dmesg lines, the unaccounted-memory numbers and the heartbeat timestamps. Ask directly: is ballooning applied to my VM, and is my plan's RAM dedicated?
- Add shock absorbers while you wait: generous swap (I added a 16GB swapfile alongside Cloudron's) and
vm.swappiness=10. This keeps apps alive through inflation spikes instead of OOMing. - If the provider will not stop it, migrate. A box that intermittently loses two thirds of its RAM will never run a busy Cloudron well, and no amount of in-guest tuning changes that.
Hopefully, this will save someone the detour of blaming ClickHouse, their apps, or the kernel first. I did all three.
-
Update — resolved, plus a few things I learned that weren't in the original post.
Outcome first: the provider acknowledged the ballooning was beyond their normal range and made two host-side adjustments. The first one didn't hold. reclaim spikes up to ~96% memused resumed that same evening. The second one worked: 40+ hours flat at ~10% memused, balloon fully deflated, right through the time windows where every earlier event clustered. Ticket closed.
New/helpful bits for anyone landing here later:
-
Verify the fix against your event windows, not the clock. Our events clustered in two daily windows (evening and early morning, presumably the node's busy periods). A VM that looks clean at lunchtime proves nothing; check sar -r across the windows where your events happened before you believe a fix.
-
Don't panic over update_balloon_stats_func in dmesg. That's the host polling guest memory stats, harmless, and arguably a good sign (they're watching, not reclaiming). The line that matters is update_balloon_size_func, that's the balloon actually inflating.
-
"Fixed once" is not fixed. If we'd accepted the first "we've adjusted the node" we'd have concluded the provider fixed it while the VM was still losing half its RAM nightly. Keep the heartbeat cron from the original post running permanently, it's one line, costs nothing, and it's the only reason we caught the failed first fix.
-
No root SSH handy? Containers can still see host memory. /proc/meminfo isn't namespaced, so from any container (docker exec, or Cloudron's cloudron exec --app …) you can read the host's MemTotal/MemAvailable and run the unaccounted-memory check from the original post. dmesg is blocked from containers, but meminfo alone will tell you if the balloon is inflated right now.
-
Ask the provider what they changed. "We doubt it will recur after the current settings" is not the same as "your VM is exempt from auto-ballooning." Get the actual change on record — it determines whether a future recurrence means the fix regressed or was never real.
We still like SSDnodes a lot!
-
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login