KVM vs Container VPS (LXC/OpenVZ): How to Tell What You Have and Why It Matters
Detect whether your VPS is KVM or a container, and see exactly which features break: custom kernels, swap/zram, LUKS, WireGuard, Docker and nested virt.
"Full root access" is not the same thing as "full control of a kernel". Two VPS plans can have identical CPU, RAM and disk specs and behave completely differently once you try to load a kernel module, create a swap file, encrypt a volume or run nested containers. The difference is the virtualization layer: a hardware-virtualized VM (KVM, Xen HVM, VMware, Hyper-V) versus an OS-level container (LXC/LXD, OpenVZ, Virtuozzo).
This article shows how to determine what you actually bought, and then walks through the specific technical limits that appear only on container-based VPS products — the ones people discover at 2 a.m., after the deployment script fails.
Quick answer
Run this on the server:
systemd-detect-virt
kvm,qemu,xen,vmware,microsoft,oracle→ you have a virtual machine with its own kernel.lxc,lxc-libvirt,openvz,docker,podman→ you have a container sharing the host kernel.none→ either bare metal, or a hypervisor that hides itself well enough that systemd can't classify it.
In a VM you can replace the kernel, load modules, create swap, use dm-crypt, run nested virtualization (if exposed) and set nearly any sysctl. In a container you generally cannot do any of those, no matter how much root you appear to have. IronBalkans sells full-root KVM instances specifically because most privacy and self-hosting workloads — VPN gateways, encrypted volumes, custom kernels — assume VM-level control.
How to identify your virtualization type reliably
systemd-detect-virt is fast but not exhaustive. Cross-check with a few independent signals.
1. virt-what and DMI data
apt install virt-what # or dnf install virt-what
virt-what
cat /sys/class/dmi/id/product_name 2>/dev/null
cat /sys/class/dmi/id/sys_vendor 2>/dev/null
A KVM guest typically reports something like QEMU / Standard PC (i440FX or Q35) or a vendor-branded product name. Containers have no DMI table at all — those files simply don't exist.
2. Kernel and module reality check
uname -a
ls /lib/modules/$(uname -r) 2>/dev/null
modprobe -n -v dummy
If /lib/modules/$(uname -r) is missing while the system is otherwise healthy, you are almost certainly running someone else's kernel. A modprobe attempt that fails with "Operation not permitted" is another strong container signal, since CAP_SYS_MODULE is never granted to guests.
3. Legacy OpenVZ markers
ls -l /proc/vz /proc/user_beancounters 2>/dev/null
/proc/user_beancounters is the classic OpenVZ 6 fingerprint. Modern products are more likely to be LXC/LXD or Virtuozzo 7 containers, but the file still turns up on cheap legacy plans.
4. Device and cgroup surface
ls -l /dev/net/tun /dev/kvm /dev/mapper 2>/dev/null
ls /sys/fs/cgroup | head
cat /proc/cpuinfo | grep -E 'vmx|svm' | head -1
Missing /dev/net/tun breaks OpenVPN and userspace WireGuard. Missing /dev/mapper control node breaks LVM and LUKS. The presence of vmx (Intel) or svm (AMD) flags means nested virtualization is exposed to you — useful if you plan to run VMs or Firecracker microVMs inside your VPS.
5. Memory and CPU reporting
free -m
nproc
cat /proc/meminfo | head -5
In a well-configured LXC host with lxcfs, /proc/meminfo reflects your cgroup limit. Without it, free may show the host's total RAM — which silently breaks every autotuning script you own (MySQL/MariaDB buffer pool sizing, PHP-FPM worker calculations, JVM heap ergonomics, PostgreSQL shared_buffers).
What actually differs in practice
Kernel version, modules and sysctls
On a VM you own the kernel: install a HWE or mainline kernel, enable BBR congestion control, load wireguard, nf_tables, tcp_bbr, dm-crypt, zram, overlay, or build a custom module. On a container you inherit whatever the host runs, including its security patch level. If the provider's host kernel is old, your wg command may exist but the interface type won't.
Sysctls are the second surprise. Many net.* and vm.* knobs are read-only inside containers, or namespaced only partially. Attempting sysctl -w vm.swappiness=10 or changing fs.file-max often fails outright. That matters a lot for the kind of tuning described in low-RAM VPS tuning with swap, zram and the OOM killer: swap files, zram devices and earlyoom behaviour all assume you control kernel memory management. In a container, memory pressure is handled by the host's cgroup limits, and your process gets killed without any of the local levers you'd expect.
Firewalling
nftables and iptables inside containers depend on the host exposing the right namespaces, capabilities and conntrack modules. Some providers block packet filtering entirely and expect you to use their control panel firewall. Others allow it but with quirks — for example missing nft sets, or connection tracking shared in ways that make rate limiting unreliable. If you plan to build a serious ruleset like the one in the nftables baseline firewall guide, verify with a harmless test rule before you design around it:
nft add table inet test && nft delete table inet test && echo "nftables works"
Disk encryption and block devices
Containers do not get block devices. You get a directory on the host's filesystem, mapped into your namespace. That means no LUKS, no LVM, no ZFS pools of your own, no cryptsetup, and no dropbear-based remote unlock. If encryption at rest is part of your threat model, read the honest assessment in what LUKS really protects on a remote server — but note that on a container VPS the question doesn't even arise, because the primitive isn't available.
VPN and tunnel workloads
This is the most common failure. Kernel WireGuard needs the module in the host kernel plus permission to create the interface in your netns; unprivileged containers frequently refuse. Userspace fallbacks (wireguard-go, OpenVPN, tun2socks) need /dev/net/tun, which many container plans don't pass through. If your goal is a personal VPN endpoint, a VM avoids the entire class of problem — the setup in the self-hosted WireGuard VPN guide assumes you can create interfaces, set NAT rules and adjust MTU freely.
Containers inside containers
Docker or Podman inside LXC requires nesting enabled by the provider, a compatible storage driver (overlayfs on overlayfs is not always allowed), and often cgroup v2 delegation. It can work well on LXD hosts that configure it deliberately, and fail confusingly everywhere else. Even when it runs, the firewall interaction gets harder to reason about — see why published Docker ports bypass your firewall for the base problem, and imagine debugging it through two layers of network namespaces.
Resource accounting and noisy neighbours
VMs expose CPU steal time in top and /proc/stat, which is the single most useful signal for detecting an oversubscribed node. Containers usually don't: contention shows up as cgroup CPU throttling (cpu.stat → nr_throttled, throttled_time) or simply as slower wall-clock time with no obvious culprit. If you want to evaluate a node's real behaviour, the methods in how to tell if your VPS is oversold apply differently: on containers, focus on I/O latency, cgroup throttle counters and scheduler wait rather than steal.
Isolation and security posture
Be precise here, because both models are frequently oversold.
A container shares one kernel with every other tenant on the node. A single kernel privilege-escalation bug can, in principle, affect all of them; the isolation is namespaces plus cgroups plus seccomp, not a hardware boundary. A VM has a narrower guest-to-host interface (virtual devices, hypercalls), which historically has been harder to break out of than the full Linux syscall surface — but hypervisor and device-emulation vulnerabilities do exist.
What neither model changes: the provider's host administrator can read your RAM and your disk. Encryption of a running VM's disk does not hide keys held in guest memory. Virtualization type affects the difficulty of tenant-to-tenant attacks; it does not create privacy from the infrastructure owner. Anyone building a threat model should treat the host as trusted-by-necessity and reduce what the server knows about them in the first place — for example through minimal logging and payment separation, as covered in VPS log minimization.
When a container VPS is genuinely fine
Container plans are cheaper per GB of RAM for a reason: density. They're a reasonable choice when:
- You run standard userspace software: nginx, a PHP or Python app, PostgreSQL, a static site, a small game server, a monitoring agent.
- You don't need custom kernel modules, VPN interfaces, block-level encryption or nested virtualization.
- You accept that the kernel patch level is the provider's decision.
- The workload is stateless enough that you can move it quickly if the node degrades.
They are the wrong tool when the workload is the kernel: VPN endpoints, packet-level networking, encrypted volumes, Docker-heavy stacks, anything needing /dev/kvm, or storage experiments with ZFS and LVM snapshots.
Common mistakes
Trusting the product page wording. "Full root", "dedicated resources" and even "virtual server" are marketing terms, not virtualization types. Only look at what systemd-detect-virt and virt-what report on the running instance.
Assuming free output is authoritative. On containers without lxcfs, autotuners read host RAM and configure a database that immediately gets OOM-killed. Set memory limits explicitly in application config instead of relying on detection.
Planning encryption you can't implement. Buying a cheap container plan for an "encrypted" file store, then finding cryptsetup returns Cannot initialize device-mapper.
Confusing hardware support with permission. vmx in /proc/cpuinfo and a readable /dev/kvm are two different things; nested virtualization needs both, plus a host that enabled it.
Ignoring kernel patch responsibility. On a VM, unpatched kernels are your problem — install updates and reboot. On a container, you depend entirely on the provider rebooting the node, and you get no notification when a kernel CVE lands.
Comparison
| Capability | KVM / hardware VM | LXC / OpenVZ container |
|---|---|---|
| Own kernel, own upgrades | Yes | No, host kernel |
modprobe custom modules |
Yes | No |
| Swap file / zram | Yes | Usually no |
| LUKS, LVM, ZFS | Yes | No block devices |
| Kernel WireGuard / TUN | Yes | Provider-dependent |
| nftables / iptables | Yes | Often limited |
| Nested virtualization | If exposed | No |
| Full sysctl control | Yes | Partial, many read-only |
| Steal-time visibility | Yes | Usually no |
| Cost per GB RAM | Higher | Lower |
| Boot time / density | Slower boot | Very fast |
FAQ
Does KVM guarantee I'm not oversold? No. Hypervisors can oversubscribe CPU and RAM just as aggressively. KVM only guarantees kernel-level control and gives you the steal-time metric to measure contention.
Can I install a different OS on a container VPS? Only what the provider templates allow, and only distributions compatible with the host kernel. No FreeBSD, no custom kernels, no ISO installs. VMs let you boot arbitrary ISOs when the provider offers console and media attachment.
Is systemd-detect-virt ever wrong?
It can return none on hypervisors that deliberately mask CPUID and DMI. Combine it with virt-what, DMI files, module availability and timing anomalies before concluding you're on bare metal.
Does container hosting leak information to neighbours?
Process lists, hostnames and mounts are namespaced, so basic separation holds. But shared kernel counters, /proc/cpuinfo, clock sources and timing side channels give more shared surface than a VM does. Treat a container as a weaker boundary and don't store secrets there that would be catastrophic if a kernel bug were exploited.
Should I re-verify after a provider migration? Yes. Node moves and plan upgrades occasionally change the underlying platform. Re-run the detection commands after any maintenance window, and include the check in the post-migration validation steps from your VPS migration checklist.
Conclusion
Decide what your workload needs from the kernel before comparing prices. If you need swap and zram tuning, LUKS volumes, kernel WireGuard, real firewalling, nested containers or your own patch schedule, only a hardware-virtualized VM will do — and you should verify the virtualization type on the running instance rather than trusting the order page. If your app is ordinary userspace software and you're comfortable letting the provider own the kernel, a container plan buys you more RAM for the same money.
Spend two minutes running systemd-detect-virt, virt-what, ls /dev/net/tun /dev/kvm, and a throwaway nft rule on any new server. It's the cheapest way to avoid rebuilding your stack on a platform that can't run it.
