Bali, Indonesia
Insights

Why Your App Uses More RAM After Moving a VPS from OpenVZ to KVM

October 21, 2026
0:00
You move a site from an OpenVZ VPS to a KVM VPS with the same advertised RAM, run the same app, and memory usage looks noticeably higher. Sometimes swap starts filling within a day, and the obvious conclusion is that the new server is worse or the app has a leak. Usually neither is true. The two platforms count memory differently, so the same number on the plan page does not mean the same usable headroom. This question comes up a lot at the support desk right after migrations, mostly because nothing in the app changed. The difference is in what the platform does underneath it.
OpenVZ is container virtualization. Every container on a host shares one Linux kernel, the host's kernel. Your VPS was really an isolated group of processes with a memory limit attached. Much of the kernel's own work, such as managing hardware, drivers, and a large part of the memory bookkeeping, happened on the host, outside the number you saw in free. KVM is full virtualization. Your VPS boots its own kernel inside a virtual machine, and that kernel lives inside your RAM allocation. Kernel code, slab caches (memory the kernel uses for its internal objects), page tables, and kernel threads all come out of the same pool your app uses. Depending on the kernel and boot parameters, some memory may also be reserved at boot, so MemTotal inside the guest can be lower than the plan says. None of this is waste. It is overhead that used to sit on someone else's books. The OS template matters too. A fresh KVM image can run services the old container never had, depending on the distro and how the image was built. It is worth listing running services after the move rather than assuming the base system is as lean as before.
Many OpenVZ plans were sold with two numbers: guaranteed memory and burst memory. The guarantee was what the host promised to keep available. Burst was extra memory the container could use when the host had spare capacity. How this was enforced depends on the OpenVZ version: older releases used a system called user_beancounters, while newer ones moved to cgroup-based limits and a mechanism called vSwap. In practice, an app could run above its guarantee for long stretches without anyone noticing. KVM has no equivalent. The VM gets a fixed amount of RAM, and the guest kernel cannot borrow more from the host. If MySQL, PHP-FPM, and a cache daemon were tuned on OpenVZ while burst memory was quietly absorbing the excess, the same settings on KVM can push the VM into real memory pressure. Before you tear down the old server, check what it really used. As root on an OpenVZ container, cat /proc/user_beancounters shows held and maximum values per resource, plus a failcnt column that counts how many times a limit was hit. Look at peak usage, not the plan name, when you size the new VPS. Database settings deserve the most attention here. If InnoDB was sized generously on the old box, the article on checking whether your InnoDB buffer pool size fits the server walks through right-sizing it.
On KVM the guest kernel manages its own page cache, which is file data kept in RAM so the next read does not hit disk. Linux fills spare memory with cache by design and drops it when applications need the space. Inside an OpenVZ container, depending on the version and host setup, page cache was either accounted differently or barely shown, so the old free output often looked cleaner than it really was. So a KVM guest showing large buff/cache and a small free column is usually healthy. The number to watch is available (or MemAvailable in /proc/meminfo), which estimates how much memory can be handed to applications without swapping. If that misunderstanding sounds familiar, there is a separate piece on why a small free column in free -m does not mean RAM is running out.
On OpenVZ with vSwap, the swap you saw inside the container was usually virtual. When the container went over its RAM limit, the host handled it, often by slowing the container down, and no real swap device existed inside it. On KVM, swap is a real file or partition on your virtual disk, and the guest kernel decides when to use it. Linux will swap out idle anonymous memory, such as pages a process allocated once and never touches again, even when RAM is not full. That frees room for page cache. The vm.swappiness setting controls how willing the kernel is to do this. It defaults to 60 on most distributions, but check yours. Some swap in use with no ongoing activity is normal and not worth worrying about. What matters is sustained swap traffic. In vmstat, the si and so columns show memory swapped in and out per second. If they stay above zero for minutes while available keeps shrinking, the VM is under real pressure and the site will feel slow, because every swapped page now waits on disk I/O. The other clear signal is the kernel OOM killer. Inside your own VM it picks a process and kills it, which on a web server is often MySQL.
BASH
# Read-only checks, safe to run on the new KVM VPS
free -m
grep -E 'MemTotal|MemAvailable|Committed_AS|SwapTotal|SwapFree' /proc/meminfo
swapon --show
cat /proc/sys/vm/swappiness
# 6 samples, 5 seconds apart; watch the si and so columns
vmstat 5 6
# Look for past OOM kills (use dmesg if journald is not available)
journalctl -k | grep -i 'out of memory'
Committed_AS is worth a glance too. It shows how much memory processes have asked for, which can be well above what they currently use. If it sits far above MemTotal, a traffic spike that makes those processes touch their allocations can tip the VM into swapping.
Start from real numbers, not the old plan name. Take the peak memory the app used on OpenVZ, including any burst it was quietly relying on. Add room for the guest kernel and its base services, which you can see on the new VM before the app is fully loaded. Then leave space for page cache, because a database or busy site with no room for cache reads from disk far more often. If that total is above the KVM plan, you have two honest options: lower the app's memory settings (worker counts, buffer sizes, cache daemon limits) or move up a plan. Treat swap as a safety net, not capacity. A modest swap file gives the kernel somewhere to put idle pages and buys time before the OOM killer acts, but a VM that needs swap to hold its working set will be slow no matter how much swap it has. The usual steps are to create a file of the size you want, restrict it to root with chmod 600, format it with mkswap, enable it with swapon, and add it to /etc/fstab so it survives a reboot. The exact way to create the file depends on the filesystem. fallocate works on many setups, some need dd, and btrfs needs extra steps, so check your distro's documentation first. If you change vm.swappiness, do it after watching real behavior for a while, and remember that lowering it does not create memory. It only changes which pages the kernel gives up first. A practical way to approach the first week after a move: check available and si/so during normal traffic and again at peak, scan the kernel log for OOM kills, and compare against what the old container really used. If available stays comfortable and swap activity stays quiet, the higher usage figure is just KVM being honest about the kernel and cache it now carries. If not, resize the app or the plan, not the swap file.