Homelab & Hardware

LXC vs VM on Proxmox: Which Should You Actually Use?

"Containers are lighter, VMs are more isolated" is true and also not the part that actually decides anything.

Most of what runs on my own three-node cluster is Docker inside privileged Debian LXC containers with nesting turned on, not VMs, so this isn't an "always use a VM" argument. It's the real decision framework: what actually differs at the kernel level, where the security story is more nuanced than either side's fans admit, and the storage, backup, and migration gotchas that only show up after you've already picked wrong.

A lot of what ranks for this exact search right now is templated "2026 guide" content with no named author and no real numbers behind it. Everything below is sourced from Proxmox's own documentation and forum, not that.

The Real Difference: Shared Kernel vs. Independent Kernel

An LXC container (Proxmox calls it a CT) uses the host's own kernel. Isolation comes from Linux namespaces (PID, network, mount, and a few others) and cgroups for resource limits, with AppArmor profiles and seccomp filters restricting which syscalls a container can make. There's one isolation layer between the container and the host: the kernel itself.

A VM runs its own independent kernel on top of KVM, Linux's hardware-assisted hypervisor, with QEMU emulating or paravirtualizing the hardware underneath it. That's two isolation layers: the hypervisor, and a guest kernel that has no direct access to the host's own kernel at all. That's the entire reason a VM escape is a categorically rarer bug class than a container escape, and it's also why a VM has real overhead a container doesn't: booting an independent kernel takes real time and real memory that a shared-kernel container skips entirely.

Everything else in this article, security, performance, backups, migration, follows from that one structural fact. It's worth having clearly in mind before the tradeoffs below start sounding like a checklist instead of consequences of how each one actually works.

The Decision Framework

Reach for LXC when

The workload is a single trusted Linux service that's happy running on the host's own kernel: a reverse proxy, Pi-hole, Syncthing, most single-purpose homelab tools. You want fast boot, low idle overhead, and the ability to run a lot of them on modest hardware. Networking is also simpler here than people expect: an LXC container gets a real interface on the host bridge rather than being stuck behind Docker's default NAT layer.

Reach for a VM when

The workload needs a different kernel or a non-Linux OS entirely, needs a real security boundary because you don't fully trust it, needs full PCIe device passthrough where the device disappears cleanly from the host's own view, or is an appliance-style OS that requires it outright. TrueNAS needs its own ZFS stack and HBA-level control a container can't give it. Home Assistant OS specifically (not the plain Home Assistant Container) needs a VM because its own Supervisor layer manages the OS underneath it. Both guides on this site cover the reasoning in more depth.

When you're not sure

Default to a VM. LXC is the optimization you reach for once you know a workload doesn't need what a VM provides, not the default you fall back to because it's lighter. Picking LXC for something that actually needed kernel- or OS-level isolation is a much more annoying mistake to unwind later than the reverse.

Privileged vs. Unprivileged: The Security Reality

Unprivileged is Proxmox's own default and recommendation for new containers. Root inside the container maps to an unprivileged UID on the host, so most kernel-level issues turn into a random unprivileged account being compromised rather than real host root. Privileged containers are labeled unsafe by upstream LXC itself and are meant for trusted environments only, or for the specific cases where a workload genuinely needs root-equivalent host access, which is part of why Docker-in-LXC setups often still run privileged in practice.

Where the generic advice oversimplifies: unprivileged isn't a hard security wall, because the container is still sharing the host's kernel either way. Two real examples from the last year make this concrete. A 2025 incompatibility between AppArmor and a newer runc release (CVE-2025-52881) broke Docker and Podman inside LXC containers across the ecosystem until community-shipped AppArmor profile workarounds fixed it. And a 2026 kernel local-privilege-escalation bug nicknamed "Dirty Frag" (CVE-2026-43284) triggered real uncertainty, even among Proxmox's own staff on the forum, about whether it enabled a true escape from a standard unprivileged container or just elevated to a still-unprivileged mapped UID. Proxmox shipped fixed kernels within weeks either way.

Worth knowing too: Proxmox VE 9's tightened AppArmor confinement has broken things like GPU monitoring tools inside unprivileged containers even when the underlying transcoding still works, and the fix some people reach for, loosening host-wide settings or disabling AppArmor on the container, quietly erases the security benefit of choosing unprivileged in the first place. Unprivileged by default, with named exceptions, is the honest rule. "Always unprivileged, no exceptions" is not.

Performance: What the Numbers Actually Show

Memory and boot time aren't close. An idle LXC container typically sits around 30 to 60MB of RAM; a minimal VM runs 150MB or more before it's done anything. Boot time follows the same pattern: a container starts near-instantly with no BIOS or bootloader sequence to run through, while a VM boots a real kernel from scratch every time.

Raw CPU throughput is murkier than the usual "containers are faster" claim suggests, and there's a genuinely useful cautionary story behind it. A Proxmox forum benchmark that got some traction found a VM outperforming an equivalent LXC container by 20 to 25 percent on identical hardware, tested on both a Ryzen 9 5950X and a Xeon E5-2667v2. A Proxmox staff member traced it to Retbleed mitigation overhead specific to the kernel version in use at the time, and the original poster later confirmed the gap disappeared entirely after upgrading to Proxmox VE 8. The lesson isn't "VMs are actually faster," it's that a lot of the LXC-vs-VM performance folklore circulating online is really a snapshot of one kernel version's overhead, not a fixed property of either technology.

I/O generally favors LXC by structure rather than benchmark: a container reads and writes through the host's own storage stack directly, with no virtio-blk or virtio-scsi translation layer in between. That's part of why running ZFS inside a VM on top of a Proxmox host's own ZFS-backed virtual disk is a known anti-pattern (ZFS on top of ZFS, with the write amplification that implies), and why the honest storage answer is usually either full HBA passthrough to a VM or a straightforward bind-mount into a container, not a VM with a virtual disk sitting on host-managed ZFS.

Where People Get Burned: Backups, Storage, and Migration

Backup behavior actually differs, not just in degree

A VM's snapshot-mode backup works on any storage type, using live copy-before-write tracking. A container's snapshot-mode backup requires the underlying storage to natively support snapshots, a real limitation VMs don't share. And by default, a container's additional mountpoints beyond its root disk are excluded from vzdump backups entirely, an easy way to quietly lose data if you assumed backing up the container backed up everything attached to it.

Passthrough disks vanish from Proxmox's own view either way

Pass an HBA or a disk through to a VM (for TrueNAS, say) and it disappears from Proxmox's own storage view, including from vzdump backups. A container's bind-mounted storage stays visible to the host instead, which is simpler but means it's layered on the host's own filesystem rather than independently managed the way a VM-hosted NAS OS wants.

Migration favors containers for speed, VMs for reach

A container migrates and restores faster since there's no RAM or CPU-state snapshot to move. A VM supports genuine live migration with full memory-state transfer between hosts, which matters more in a clustered homelab doing anything resembling high availability. Either way, passed-through PCIe hardware pins that guest to a specific physical host and breaks migration regardless of which technology it's attached to.

Running Docker in LXC (What I Actually Do), and What's New in Proxmox VE 9.1

Docker inside an LXC container works, and it's most of what my own homelab runs. It needs the nesting=1 container feature enabled, plus keyctl=1 if the container is unprivileged, since Docker relies on keyctl() calls that would otherwise fail. It's a solid fit for single-host homelab use. It's also more fragile than Docker in a VM in a way that's easy to forget until it bites: the AppArmor/runc incompatibility mentioned above broke Docker-in-LXC across the board in 2025, purely because the container shares the host's kernel and security modules. Docker running inside a VM's own independent kernel was never exposed to that particular bug at all.

Proxmox VE 9.1 added something worth knowing about if you're setting up a new homelab rather than following an older guide: you can now pull a standard OCI or Docker registry image directly and run it as a lean, Proxmox-managed application container, sitting alongside the traditional full-system LXC templates. For a simple single-app workload, that's a genuinely new middle option between "install a full Docker daemon inside a general-purpose container" and "just use a VM." It doesn't change anything about when a real VM is still the right call. It does mean some of the older "you need a VM or a full LXC with Docker installed" framing is now slightly out of date.

The Counter-Intuitive One: GPU Sharing Often Favors LXC

Full PCIe GPU passthrough to a VM binds the entire physical device to that one VM exclusively, unless you're set up for SR-IOV or vGPU, which is real additional complexity most homelabs skip. LXC device passthrough instead bind-mounts the GPU's device nodes into the container and uses cgroup device rules to grant access, which lets the same physical GPU be shared across multiple containers at once, for something like Jellyfin and Frigate both using hardware transcoding on one card. The tradeoff is real: the container is now tied to the host's exact driver version with no isolation between them, and Proxmox VE 9's tighter AppArmor confinement (see the security section above) has broken some GPU monitoring tooling inside unprivileged containers even when transcoding itself keeps working. For shared, non-exclusive GPU use, LXC is usually the simpler and more practical choice. For a workload that needs the whole device to itself, isolated drivers, or vGPU, that's a VM.

So What Would I Actually Run?

Depends on the workload, not a blanket rule either direction:

  • A single trusted service, or Docker for homelab-scale workloads: unprivileged LXC by default, privileged with nesting only when the workload specifically needs it. This is most of what I actually run.
  • TrueNAS, Home Assistant OS, or anything with its own OS-management layer: a VM, full stop. The appliance itself requires it, and fighting that isn't worth it.
  • Anything you don't fully trust, or that needs a different kernel/OS than the host: a VM. This is the actual security boundary; LXC was never meant to be one.
  • Shared GPU transcoding across multiple services: LXC device passthrough, accepting the driver-coupling tradeoff.
  • A dedicated, exclusive GPU workload, like a gaming VM or something that needs the whole device isolated: full PCIe passthrough to a VM.

A Few Things I'd Steer Away From

1. Defaulting to LXC for everything because it's lighter

Lower overhead is a real advantage, not a reason to skip the actual question of whether the workload needs kernel- or OS-level isolation. Pick based on what the workload requires, then take the overhead win where it applies.

2. Treating "unprivileged" as a hard security guarantee

It's the right default with named, real exceptions, not an absolute wall. The container still shares the host's kernel either way.

3. Assuming a container's mountpoints are all backed up automatically

Additional mountpoints beyond the root disk are excluded from vzdump backups by default. Check this explicitly rather than finding out during a restore.

4. Running ZFS inside a VM sitting on host-managed ZFS

ZFS on top of ZFS adds write amplification for no real benefit. Pass the controller through instead if the workload needs its own ZFS pool.

5. Citing an old "LXC is X% faster" number as a fixed law

The most-cited version of that claim traced back to a specific kernel's mitigation overhead and disappeared after an upgrade. Performance differences here are workload- and version-dependent, not a constant.

Frequently Asked Questions

What's the actual technical difference between an LXC container and a VM on Proxmox?

An LXC container shares the host's kernel and is isolated by Linux namespaces and cgroups, with AppArmor and seccomp restricting what it can do. A VM runs its own independent kernel on top of KVM/QEMU hardware virtualization, so there are two isolation layers between the guest and the host instead of one. That's why a container can boot almost instantly and idle at a fraction of a VM's memory footprint, and it's also why a kernel bug is a bigger deal for a container than for a VM: the container is sharing that kernel with the host.

When should I use an LXC container instead of a VM on Proxmox?

Use LXC for a single trusted Linux service that doesn't need a different kernel or OS than the host: Syncthing, Pi-hole, a reverse proxy, most single-purpose homelab tools. Use a VM when the workload needs its own kernel or a non-Linux OS, needs a real security boundary for something you don't fully trust, needs full-device PCIe passthrough with the device disappearing cleanly from the host, or is an appliance-style OS that requires it outright, like TrueNAS or Home Assistant OS. If you're not sure, a VM is the safer default; LXC is the optimization you reach for once you know the workload doesn't need what a VM provides.

Should I use a privileged or unprivileged LXC container?

Unprivileged by default, which is also Proxmox's own recommendation: container root maps to an unprivileged host user, so most kernel-level issues affect a random unprivileged account instead of real root. Privileged containers are labeled unsafe by upstream LXC itself and should be reserved for cases where you specifically need root-equivalent access to host devices, like Docker workloads that need certain device or capability access unprivileged mode won't grant cleanly. That said, unprivileged isn't an absolute shield. AppArmor's tightened confinement in Proxmox VE 9 has broken things like GPU monitoring tools inside unprivileged containers, and real kernel CVEs affecting containers have shipped in 2025 and 2026. Unprivileged is the right default, not a guarantee.

Can I run Docker inside an LXC container on Proxmox?

Yes, and it's what I run for most of my own homelab, but it needs the nesting=1 container feature enabled, and keyctl=1 if the container is unprivileged. It's fine for single-host homelab use. It's more fragile than Docker in a VM: a 2025 AppArmor/runc incompatibility (CVE-2025-52881) broke Docker-in-LXC across the ecosystem until community workarounds shipped, which doesn't happen to Docker running in a VM's own independent kernel. Proxmox VE 9.1 also added a genuinely new option: pulling standard OCI images directly as lean, Proxmox-managed application containers, which sidesteps running a full Docker daemon inside a general-purpose container for simple single-app cases.

Is an LXC container actually faster than a VM?

For memory footprint and boot time, yes, clearly: an idle container typically uses tens of megabytes against 150MB or more for a minimal VM, and it boots in a fraction of the time with no BIOS or bootloader sequence. Raw CPU throughput is less clear-cut than the folklore suggests. A widely-cited Proxmox forum benchmark found a VM outperforming an LXC container by 20 to 25 percent on identical hardware, traced to Retbleed mitigation overhead in a specific kernel version, and the gap disappeared entirely after upgrading to Proxmox VE 8. Treat any specific 'LXC is X% faster' number as workload- and kernel-version-dependent rather than a fixed law.

What's new in Proxmox VE 9.1 that changes this decision?

OCI-based LXC deployment: Proxmox VE 9.1 added the ability to pull a standard Docker/OCI registry image directly and run it as a lean, Proxmox-managed application container, alongside the traditional full-system LXC templates. It narrows the old logic of always defaulting to a general-purpose container with a full Docker install for a simple single-app workload. It doesn't replace VMs for anything that needs a real kernel boundary or an appliance OS, but it's a genuinely new middle option worth knowing about if you're setting up a new homelab in 2026 rather than following a guide written before it existed.