Firecracker vs OpenVZ/Virtuozzo: container VPS vs microVM
If you rented a $3/month VPS at any point between roughly 2006 and 2016, there is a very good chance you were not on a virtual machine at all. You were in an OpenVZ container: a slice of one shared Linux kernel, dressed up convincingly enough that `df`, `top`, and `ifconfig` all played along. OpenVZ (and its commercial sibling Virtuozzo) built an entire industry on that illusion, and it worked because the illusion was genuinely excellent — full root, your own IP, your own init, your own package manager, on a host packing several times more customers per box than any hypervisor of the era could manage.
I'm Ajay, and I built PandaStack on Firecracker microVMs — so I have an obvious bias, and I'd rather name it than pretend otherwise. This post is a fair head-to-head, because OpenVZ deserves one. It solved density better than anything that came after it, and quite a lot of what Firecracker does today is an attempt to claw that density back without inheriting the one architectural decision that eventually cornered OpenVZ: everyone shares a kernel.
What OpenVZ/Virtuozzo actually is
OpenVZ is OS-level virtualization. There is one Linux kernel on the host, patched with the machinery to carve itself into many isolated "containers" (historically VPS, later CT). Each container gets its own process tree, filesystem root, network stack, users, and — crucially for the hosting business — its own apparent view of system resources. Inside, it looks and smells like a server. `ps` shows only your processes. `/proc/meminfo` reports your quota, not the host's 256 GB. You are root. You run systemd or sysvinit, `apt install nginx`, and reboot "your server" whenever you like.
Virtuozzo is the commercial product line from the same lineage, with the management tooling, storage, and support contract that hosting providers actually bought. Historically OpenVZ shipped as a heavily patched kernel maintained outside the mainline tree; later generations moved toward mainline container primitives (namespaces, cgroups) as the kernel absorbed the ideas OpenVZ pioneered. The specifics of which generation supports what have shifted repeatedly over the years, so verify anything version-dependent against current Virtuozzo/OpenVZ documentation rather than trusting a blog post — including this one.
Resource control was OpenVZ's signature engineering. User Beancounters (UBC) exposed a per-container table of kernel-resource limits — memory pages, socket buffers, file handles, and a couple dozen more — each with a barrier, a limit, and a failure counter. When a container misbehaved, you didn't guess; you read `/proc/user_beancounters` and saw exactly which counter had a nonzero `failcnt`. Later, vSwap replaced the more baroque parts of UBC with a simpler RAM-plus-swap model that behaved more like what customers expected from a real machine. It was, and this is not a backhanded compliment, better resource accounting than most hypervisors offered at the time.
The isolation boundary: one kernel vs one kernel each
Everything else in this comparison is downstream of a single fact. In OpenVZ, when a process inside a container makes a syscall, that syscall is executed by the host's kernel — the same kernel serving every other container and the host itself. The container boundary is a set of checks inside that shared kernel: is this PID visible to you, is this mount in your namespace, does this beancounter allow the allocation. It is enforcement by careful bookkeeping, and the bookkeeping is only as good as the last code path someone remembered to check.
A Firecracker microVM inverts this. Each guest boots its own kernel image under KVM — real hardware virtualization, with the CPU itself enforcing the boundary via VMX/SVM. Untrusted code inside the guest makes syscalls to a guest kernel that is not your host's kernel, shares no state with it, and can be a completely different version. To reach the host, an attacker must first own the guest kernel (which buys them root inside a disposable VM — congratulations, you own a box that's about to be deleted), then find and exploit a bug in Firecracker's deliberately tiny virtio device model, then get past the VMM's seccomp filter and the jailer, then get past KVM and the hardware boundary underneath.
A shared kernel is a single point of failure that an entire industry agreed to call a feature. It was a reasonable trade when the alternative was booting a full VM per tenant. It stopped being reasonable when it didn't have to be.
This is not a theoretical distinction. Every local-privilege-escalation bug in the Linux kernel is, in a shared-kernel container host, a potential container-escape bug: one tenant finds a reachable flaw in a syscall path and lands in the host kernel's address space, which is also every neighbor's kernel. The historical mitigation was to patch fast and hope, and OpenVZ operators got very, very good at patching fast. But "our security model is a maintenance treadmill" is a different proposition from "the CPU stops you."
Kernel version freedom: the part that actually killed OpenVZ for many
Security is the headline, but the practical daily annoyance was always the kernel. In OpenVZ your container does not have a kernel — it borrows the host's. So you cannot load a kernel module. You cannot run a different kernel version than your provider chose. You cannot use a kernel feature your host's kernel is too old for, and you cannot avoid a regression your host's kernel is too new for. `modprobe` fails. Custom iptables/nftables modules, WireGuard before it was mainlined, FUSE, TUN/TAP, Docker-in-container, eBPF, newer io_uring — every one of these was, at some point, a support ticket that ended with "not supported on OpenVZ, please upgrade to a KVM plan."
Here's the difference as your guest sees it. On OpenVZ, `uname -r` inside the container reports the host's kernel, because it is the host's kernel:
# --- On the OpenVZ hardware node (the host) ---
$ uname -r
2.6.32-042stab145.3 # the provider's patched OpenVZ kernel
$ vzctl enter 101
# --- Now INSIDE container 101 ---
[ct-101]$ uname -r
2.6.32-042stab145.3 # ...identical. You are borrowing it.
[ct-101]$ modprobe wireguard
modprobe: ERROR: could not insert 'wireguard': Operation not permitted
# There is no kernel of yours to insert it into.
[ct-101]$ cat /proc/user_beancounters | head -5
# resource held maxheld barrier limit failcnt
# kmemsize 2841049 3115008 14372700 14790451 0
# privvmpages 14002 18311 131072 144179 27 <-- you hit a wall
# Excellent accounting. Also: the wall is in someone else's kernel.In a Firecracker microVM, the kernel is a file you chose. You hand the VMM a kernel image, a rootfs, and a boot args string, and it starts a guest that has no opinion whatsoever about what the host is running. Two guests on the same host can be on entirely different kernel versions, with different modules, different scheduler tunables, different security defaults:
# Firecracker: the guest kernel is an argument, not an inheritance.
# Point the VMM at YOUR kernel image over its API socket.
curl --unix-socket /tmp/fc.sock -i \
-X PUT 'http://localhost/boot-source' \
-H 'Content-Type: application/json' \
-d '{
"kernel_image_path": "/opt/kernels/vmlinux-6.1",
"boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
}'
curl --unix-socket /tmp/fc.sock -i \
-X PUT 'http://localhost/drives/rootfs' \
-H 'Content-Type: application/json' \
-d '{
"drive_id": "rootfs",
"path_on_host": "/var/lib/pandastack/rootfs.ext4",
"is_root_device": true,
"is_read_only": false
}'
# Two vCPUs, 2 GiB. The host could be on 5.15 or 6.8 — the guest neither
# knows nor cares, which is the entire point.
curl --unix-socket /tmp/fc.sock -i \
-X PUT 'http://localhost/machine-config' \
-H 'Content-Type: application/json' \
-d '{ "vcpu_count": 2, "mem_size_mib": 2048 }'
curl --unix-socket /tmp/fc.sock -i \
-X PUT 'http://localhost/actions' \
-H 'Content-Type: application/json' \
-d '{ "action_type": "InstanceStart" }'Attack surface: the whole syscall table vs a handful of virtio devices
Measure the boundary by what untrusted code can actually touch. In an OpenVZ container, hostile code can reach essentially the entire Linux syscall interface — hundreds of syscalls, each with its own ioctls, flag combinations, and decades of accumulated behaviour, all executing in the host kernel. Filesystem code, network stack, memory management, the whole thing. That surface is enormous, actively developed, and generously supplied with CVEs. OpenVZ narrowed it with capability drops and its own patches, but the fundamental shape is: attacker code talks directly to the kernel that also owns the host.
Firecracker's exposed surface to a hostile guest is a short list you can nearly recite from memory: a virtio block device, a virtio net device, virtio-vsock, a serial console, a minimal keyboard controller for reset, and a handful of MMIO paths. No PCI enumeration, no USB, no BIOS, no legacy device zoo, no graphics. The VMM itself is written in Rust, runs as an unprivileged host process, and applies a seccomp filter that permits only the syscalls it demonstrably needs. AWS did not build it that way for elegance points — they built it because Lambda and Fargate run strangers' code on shared fleets, and "the syscall table" is not an acceptable answer to "what's between tenants?"
Density and overcommit: where OpenVZ genuinely won
Now the part where OpenVZ deserves its flowers. Because containers share one kernel, they also share one page cache, one slab allocator, one set of kernel data structures, and — critically — one copy of every shared library page that happens to be identical across containers. A hundred containers all running the same distro's nginx aren't a hundred copies of glibc in RAM; they're one, mapped many times. Add the fact that a container's "memory usage" is genuinely just its processes, with no guest kernel, no guest page cache, and no ballooning games, and you get overcommit ratios that classic hypervisors simply could not match.
That's why the cheap VPS market ran on OpenVZ. The economics were not close. Providers could sell far more "servers" per physical box than KVM allowed, memory that a container wasn't touching was memory the host could hand to a neighbor, and beancounters/vSwap gave operators fine-grained control over exactly how far to push it. If your only metric is tenants-per-host for cooperative workloads, OpenVZ beat every hypervisor of its generation, and it wasn't a fluke — it was the direct payoff of the shared-kernel design.
MicroVMs claw a surprising amount of that back, though not all of it, and it's worth being precise about how. A Firecracker guest's kernel and device model are small, so the fixed per-guest overhead is a few megabytes rather than the hundreds you'd associate with a traditional VM. Snapshot-restore does the heavier lifting: on PandaStack, when a create restores a baked snapshot, guest memory is mapped `MAP_PRIVATE` and paged in lazily, so identical guests share the underlying snapshot pages copy-on-write until one of them writes. The rootfs is a reflink clone rather than a copy. Fifty sandboxes from the same template are not fifty full memory images — they're one baked image plus fifty sets of divergences.
The honest scorecard: for a fleet of long-lived, heterogeneous, cooperative tenants, OpenVZ-style sharing still has a structural density edge, because a guest kernel and a guest page cache per tenant are real costs a container simply doesn't pay. For fleets of short-lived or homogeneous workloads — the AI-agent, code-execution, CI-runner, per-request shape — CoW snapshot restore closes most of the gap while keeping a hardware boundary. Different curves; know which one your workload sits on.
Boot and provisioning time
Starting an OpenVZ container is fast, because nothing boots in the hypervisor sense — no firmware, no kernel decompression, no device probing. You create namespaces around processes and start an init. That was another decisive advantage over the KVM VPS of the era, which needed a genuine multi-second boot every time.
The reason this comparison is less lopsided in 2026 is that microVMs stopped cold-booting for every create. On PandaStack, the first-ever launch of a template does a real cold boot in about 3 seconds and captures a snapshot; every create after that restores that snapshot on demand — the restore step is around 49ms, and end-to-end create is p50 179ms, p99 about 203ms. There's no warm pool of idle VMs sitting there burning money. Forking a running sandbox is 400–750ms on the same host, 1.2–3.5s across hosts. That's within the range where a hardware-isolated VM per request stops being an architectural sacrifice and starts being a default.
from pandastack import Sandbox
# A whole guest kernel, per run, for the price of a snapshot restore.
# The OpenVZ trade — 'containers are fast, VMs are slow, pick your poison' —
# is the part that stopped being true.
with Sandbox.create(template="base", ttl_seconds=300) as sbx:
r = sbx.exec("uname -r && cat /proc/1/comm", timeout_seconds=30)
print(r.stdout) # a guest kernel that is NOT the host's
print("exit:", r.exit_code)
# Load a module, run Docker, tune sysctls — it's your kernel.
r2 = sbx.exec("sysctl -w net.ipv4.tcp_congestion_control=bbr",
timeout_seconds=10)
print(r2.exit_code) # 0. Try that in a container VPS.
# Guest destroyed here. Blast radius of whatever ran: one disposable VM.Live migration and operational maturity
OpenVZ/Virtuozzo shipped live container migration early and used it hard — moving a running container between hardware nodes with a brief pause, which let operators drain a host for maintenance without customer-visible downtime. Checkpoint/restore of a process tree is genuinely difficult engineering, and the CRIU project that grew out of that work is now used well beyond OpenVZ. If "evacuate this host on a Tuesday afternoon without telling anyone" is a core requirement, that heritage is real and you should evaluate current Virtuozzo capabilities directly against their docs rather than from memory.
Firecracker approaches the same operational need from a different angle: snapshot and restore. You snapshot a running microVM's full memory and device state to a file, and restore it — later, or elsewhere. That's not identical to seamless live migration (there's a real pause, and the restore target needs the snapshot's backing files), but it composes into things container migration never did: fork a running machine into N identical copies, hibernate an idle workload to storage and pay nothing while it sleeps, or restore the same snapshot on a different host entirely. The mental model shifts from "move the box" to "the box is a file."
Side by side
- Isolation boundary — Firecracker: hardware virtualization under KVM, own guest kernel per VM. OpenVZ/Virtuozzo: OS-level virtualization — namespaces and kernel patches enforcing separation inside one shared host kernel.
- Guest kernel — Firecracker: you supply the kernel image; every guest can differ from the host and from each other. OpenVZ/Virtuozzo: no guest kernel at all — you run whatever the provider runs, and `modprobe` is not yours to call.
- Attack surface exposed to hostile code — Firecracker: a small virtio device model plus a seccomp-filtered, unprivileged Rust VMM behind the jailer. OpenVZ/Virtuozzo: the full Linux syscall table, executed by the host kernel.
- Resource accounting — Firecracker: fixed vCPU/RAM per guest, enforced by the VMM and cgroups on the host. OpenVZ/Virtuozzo: beancounters and later vSwap — famously granular per-container kernel-resource limits with visible failure counters.
- Density and overcommit — Firecracker: a few MB fixed overhead per guest; snapshot CoW and lazy paging recover much of the sharing. OpenVZ/Virtuozzo: historically the density champion — one kernel, one page cache, shared library pages across all containers.
- Start time — Firecracker: ~3s for a template's first cold boot, then snapshot restore (PandaStack: ~49ms restore, p50 179ms create). OpenVZ/Virtuozzo: very fast — no hypervisor boot at all, just namespaces and an init.
- Live migration — Firecracker: snapshot/restore and fork rather than seamless live migration. OpenVZ/Virtuozzo: mature live container migration (the CRIU lineage) — a genuine operational strength.
- Untrusted or multi-tenant code — Firecracker: designed for exactly this (the AWS Lambda/Fargate model). OpenVZ/Virtuozzo: risky by construction — a kernel LPE bug is a cross-tenant escape.
- Best fit in 2026 — Firecracker: AI agents, code execution, per-request sandboxes, multi-tenant platforms, anything you didn't write. OpenVZ/Virtuozzo: dense hosting of cooperative workloads under one administrative trust domain, where an existing deployment and its tooling already work.
Which to pick: draw the line at trust, not at size
The deciding question isn't how big the workload is or how many of them you're running. It's whether a Linux kernel bug in one tenant's hands is an acceptable outcome for everyone else on the box. If the answer is "no, obviously not," you need a hardware boundary and the conversation is over. If the answer is "everything here is mine anyway," then shared-kernel density is a legitimate optimization and you should take it.
Container-style OS virtualization is the right call when:
- Every workload on the host is first-party — your services, your team, one trust domain — so a kernel escape moves an attacker from your code to your other code.
- Density per physical host is the dominant cost driver and the workloads are long-lived, cooperative, and mostly idle.
- You have an existing OpenVZ/Virtuozzo estate with working tooling and operational muscle memory, and the migration cost is real while the threat model hasn't changed.
- Live migration for host maintenance is a hard requirement and the checkpoint/restore lineage there fits your ops model.
A microVM is the right call when:
- The code is untrusted, multi-tenant, or generated at runtime — AI agents running model-authored shell commands, code interpreters, online judges, per-customer build steps from repos you don't control.
- Tenants must not be able to reach each other even if one of them owns their own kernel, which is the whole reason the big serverless platforms landed here.
- Workloads need kernel-level freedom: their own kernel version, loadable modules, nested containers, eBPF, custom sysctls — all the tickets that historically ended in 'upgrade to a KVM plan'.
- Isolation used to be too slow to apply per-request, and snapshot-restore has quietly removed that excuse.
The honest verdict
OpenVZ was not a bad design that lost. It was a very good design that made an explicit trade — share the kernel, get extraordinary density — at a time when the alternative was booting a real VM per tenant and eating multiple seconds plus hundreds of megabytes for the privilege. Given those constraints, the trade was correct, and an entire generation of affordable hosting exists because someone made it.
What changed is that the constraint went away. Firecracker made the VM boundary small enough and cheap enough that you no longer have to choose between "isolated" and "dense," and snapshot-restore closed most of the startup gap that made containers the pragmatic default. So the modern answer is unromantic: if you trust everything on the box, container virtualization is still an efficient way to pack it, and OpenVZ was excellent at that job. If you don't — and if you're running AI-generated code, you don't, no matter how the prompt was worded — put a kernel and a CPU boundary between them, and stop calling a shared kernel a feature. As always, verify anything version-specific against the current OpenVZ/Virtuozzo and Firecracker documentation, since defaults and supported features on both sides move over time.
Frequently asked questions
Is an OpenVZ VPS a real virtual machine?
No. OpenVZ (and Virtuozzo) use OS-level virtualization: containers share the host's single Linux kernel, isolated by namespaces and kernel patches rather than by a hypervisor. It presents like a server — you get root, your own IP, your own init and package manager — but there is no guest kernel and no hardware virtualization boundary. A Firecracker microVM boots its own guest kernel under KVM, which is the difference that matters for both isolation and kernel-version freedom.
Why can't I load kernel modules or run a different kernel on OpenVZ?
Because your container doesn't have a kernel — it borrows the host's. `uname -r` inside an OpenVZ container reports the hardware node's kernel version, and `modprobe` fails because there's no kernel of yours to insert a module into. That's why WireGuard, FUSE, TUN/TAP, nested Docker, eBPF, and various sysctls were historically 'not supported on OpenVZ, upgrade to a KVM plan' tickets. In a Firecracker microVM the kernel image is a parameter you supply at boot, so each guest can run a different version from the host and from every neighbor.
Was OpenVZ's density really better than VMs?
Yes, genuinely, and it's the main reason it dominated cheap hosting. One shared kernel means one page cache, one set of kernel data structures, and shared library pages across all containers, plus no guest kernel or guest page cache per tenant — so overcommit ratios beat any hypervisor of that era. MicroVMs recover a large part of that gap through copy-on-write snapshot restore and lazy memory paging (identical guests share baked snapshot pages until they write), but for long-lived heterogeneous cooperative tenants, shared-kernel containers still carry a structural density advantage. Check current figures against vendor docs rather than trusting remembered numbers.
Is OpenVZ safe for running untrusted or AI-generated code?
It isn't the right tool for that. Untrusted code in an OpenVZ container makes syscalls directly into the host kernel — the same kernel serving every other tenant — so any reachable local-privilege-escalation bug in Linux becomes a potential cross-tenant escape. Firecracker exposes hostile code only to a guest kernel it can own harmlessly, then a minimal virtio device model, then a seccomp-filtered unprivileged VMM, then KVM and the CPU boundary. That's why AWS Lambda and Fargate run customer code in microVMs rather than shared-kernel containers.
Does Firecracker support live migration like Virtuozzo does?
Not in the same seamless form. Virtuozzo/OpenVZ built mature live container migration on checkpoint/restore (the lineage that produced CRIU), letting operators drain a host with only a brief pause. Firecracker's model is snapshot and restore: you capture a running microVM's full memory and device state to a file and restore it later or elsewhere. There's a real pause and the target needs the backing files, but it composes into capabilities container migration never offered — forking a running machine into N identical copies, or hibernating an idle guest to storage. On PandaStack a same-host fork is 400–750ms and cross-host is 1.2–3.5s.
49ms p50 cold start. Fork, snapshot, and scale to zero.