An LXC container shares the Proxmox host’s Linux kernel, has low runtime overhead, runs only Linux and cannot live-migrate. A VM boots its own kernel. Use an LXC for Linux services such as DNS filtering or Jellyfin on an iGPU; use a VM for Docker hosts, Home Assistant OS, TrueNAS, Windows and firewalls.
Contents
By LK Wood IV · 2026-05-30 · Updated 2026-09-28 · ~10 min read · St. Louis County, MO
The kernel decides it. A Proxmox VM boots its own kernel on virtual hardware; an LXC container, which Proxmox calls a CT and manages with pct (the Proxmox Container Toolkit), runs on the host’s. So a container runs only Linux and cannot live-migrate, while a VM runs Windows or FreeBSD as readily as Linux and can take a whole PCI device. The Proxmox behaviour on this page is from Proxmox VE 9.2 (released May 21, 2026) and its 9.2.12 manual, dated September 18, 2026.
Here’s what I use in my own homelab, why, and where the lines are.
Which one for each service
The Proxmox capacity planner estimates how many guests of each kind your host can hold.
| Service | Put it in | Why | Source |
|---|---|---|---|
| Docker host (Compose stacks) | VM | Proxmox’s container chapter keeps nesting application containers “inside a Proxmox QEMU VM” as a recommended practice for maximum isolation and live migration. Docker in an unprivileged LXC runs with nesting=1,keyctl=1; I would still put a Compose stack in a VM. Size it with the Docker RAM planner. | pct chapter, pct.conf(5) |
| Home Assistant | VM running HAOS | Home Assistant calls HAOS “the recommended installation type for most users”. A container install has no apps, and Thread and Z-Wave are controlled by apps. HAOS VM steps | Home Assistant installation |
| Plex or Jellyfin on an Intel iGPU | LXC | Pass /dev/dri/renderD128 in as a device with the render group’s GID; the driver stays on the host. Jellyfin documents this route for Proxmox VE 8 and newer. Plex’s hardware transcoding also needs a Plex Pass. | Jellyfin, LXC on Proxmox, Plex |
| Plex or Jellyfin on a dedicated NVIDIA card | VM, when one guest gets the card | With PCI passthrough the VM owns the card: “you cannot use that device anymore on the host or in any other VM”. Sharing it with an LXC instead needs the NVIDIA kernel driver on the host, since a container runs on the host’s kernel; Jellyfin documents NVIDIA for Docker, Podman and WSL2 and says other virtualizations “are not verified”. GPU passthrough steps | qm chapter, pct chapter, Jellyfin, NVIDIA |
| NAS operating system (TrueNAS) | VM with the disk controller passed through | For data you care about, TrueNAS says “the storage controller must be assigned to the VM via PCI passthrough”. TrueNAS lists VM snapshots and live migration among what passthrough gives up, and ZFS snapshots inside TrueNAS still work; Proxmox’s own docs say a passed-through device blocks live migration. | TrueNAS blog, qm chapter |
| File shares from the host’s own ZFS pool | LXC with a bind mount | The host keeps the pool, and a bind mount hands one directory to the container: pct set 100 -mp0 /mnt/bindmounts/shared,mp=/shared. vzdump never backs up a bind mount, so the pool needs its own backup. ZFS pool setup | pct chapter, vzdump chapter |
| Windows guest | VM | “It is not possible to run other operating systems like, for example, FreeBSD or Microsoft Windows inside a container.” | pct chapter |
| Firewall (OPNsense, pfSense) | VM | Both are built on FreeBSD, so the row above rules out a container. Netgate’s Proxmox guide builds pfSense as a VM on two Linux bridges and says checksum offloading “must be disabled” on VirtIO interfaces. Prefer bare metal? See OPNsense on a mini PC. | Netgate, Proxmox VE, Netgate, why FreeBSD, OPNsense |
| DNS filter (Pi-hole, AdGuard Home) | LXC | A plain Linux service with no module, device or other OS to ask for, and a container’s runtime cost is “low, usually negligible”. AdGuard Home in an LXC | pct chapter |
A service not listed goes through the decision tree further down.
What each one is
A VM is KVM with QEMU: virtual hardware and its own kernel, so it can run Windows or FreeBSD, load modules for its guest kernel and take a PCI device whole. A container runs in its own namespaces on the host’s kernel. Proxmox puts a container’s runtime cost at “low, usually negligible” and names the price in the same place: only Linux runs in one, and its access to host resources is restricted.
Where each one wins
LXC
It carries no guest kernel and no QEMU process. Bind mounts give a container a host directory “with practically zero overhead”, and a render node such as /dev/dri/renderD128 can be passed in as a device while the host keeps the GPU and its driver. The web interface gained that device passthrough in Proxmox VE 8.2. The API and CLI had it from 8.1.
VM
Its own kernel and modules. A container runs on whatever modules the host kernel has, and no feature flag changes that: pct.conf(5) lists force_rw_sys, fuse, keyctl, mknod, mount and nesting, nothing that loads a module. WireGuard is not a reason for a VM. It has been in the mainline kernel since Linux 5.6 (March 29, 2020). Proxmox VE 9.2 ships kernel 7.0, and the WireGuard tutorial runs it in a privileged container; keep that setup to trusted code. A module the Proxmox kernel does not carry, a different ZFS version or an out-of-tree NIC driver is a reason.
Other operating systems. Windows and FreeBSD, and the firewall and NAS appliances built on them.
A whole PCI device. A GPU, an HBA or a NIC passed through belongs to that VM alone.
Live migration. In a cluster, qm migrate <vmid> <target> --online moves a running VM — as long as it has no local resource that cannot be migrated: passed-through PCI or USB devices block it, local disks do not. A running container cannot be live-migrated. You can do a restart migration instead (pct migrate --restart): it shuts the container down (killing it after 180 seconds by default), copies it and starts it on the target.
Isolation. A container uses the host’s kernel, which Proxmox says “exposes an attack surface for malicious users”, and “in general, full virtual machines provide better isolation.” A VM’s boundary is KVM and QEMU. I would put untrusted or public-facing code in a VM.
Resource overhead numbers
Here’s a concrete comparison measured on a Beelink SEi12 Pro (N150, 16GB RAM, 512GB NVMe) running Proxmox VE 8.x. That was the current release when these numbers were taken. VE 9 has shipped since and Proxmox’s lifecycle table puts VE 8’s end of support at August 2026. The container-versus-VM overhead these figures show is structural rather than version-specific, so the shape holds. Treat the absolute megabytes as 8.x-era:
| Metric | LXC (Debian 12, idle) | VM (Debian 12, idle, virtio) |
|---|---|---|
| RAM used by guest | ~55 MB | ~280 MB |
| CPU overhead | <1% | 1–3% |
| Storage IO vs bare metal | ~98% | ~94–97% |
| Network throughput vs bare metal | ~99% | ~97% |
| Boot time | 0.5–1.5 seconds | 8–20 seconds |
| Backup size (Debian + 1 app) | ~400 MB | ~2.0 GB |
Numbers are representative of Proxmox 8.x with virtio drivers. The storage IO gap closes significantly when using virtio-blk with io_uring.
What I run in LXC vs VM
In my own Proxmox setup, the split is roughly:
LXC containers:
- Home Assistant (isolated HAOS-style config on a Debian container)
- Vaultwarden (no kernel needs, pure userspace app)
- Nginx reverse proxy
- Uptime Kuma
- Immich photo server
- Pi-hole DNS
- Gitea
- Prometheus + Grafana stack
- Any service that’s “just a process” — no special kernel requirements, no GPU, trusted code
Virtual machines:
- TrueNAS Scale (needs its own ZFS kernel, storage needs isolation)
- Windows 11 (gaming and testing)
- Any LLM inference node with RTX 5080 GPU passthrough
- OPNsense (firewall OS — needs network kernel control and isolation is mandatory)
- Any publicly-exposed service handling user data
The pattern: if the workload is a Linux service that doesn’t need kernel modules, GPU access, or strong isolation, it’s an LXC container. If it needs any of those, it’s a VM.
The decision tree
New workload in Proxmox?
│
├─ Docker/Compose stack or Home Assistant OS? → VM
│
├─ Windows, FreeBSD or any non-Linux OS? → VM
│
├─ One guest owns a whole PCI device (GPU, HBA, NIC)? → VM
│
├─ Needs its own kernel or a module the host lacks? → VM
│
├─ Untrusted or public-facing code? → VM
│
├─ Must keep running while it moves between cluster nodes? → VM
│
└─ Everything else → LXC container
(a Linux service, trusted code; an iGPU render node or a host directory can be passed in)
When a service could go either way, I would start it in a VM. Moving a working service into a container later is a planned job; finding out three months in that a container needs the whole GPU is a rebuild.
Proxmox-specific gotchas
Unprivileged is the default. The container’s root UID 0 is mapped to an unprivileged user on the host, and “This is the default option when creating a new container”, in the web interface, where the Create CT wizard’s “Unprivileged container” box starts ticked, and in pct create alike (pct(1): “For creation, the default is 1”). The LXC project calls a privileged container unsafe. It does not treat new escapes from one as security issues “worthy of a CVE and quick fix”. Keep privileged containers for trusted work.
NFS or SMB inside a container. The NFS and SMB/CIFS boxes in a container’s Features dialog are marked “privileged only”, and pct.conf(5) warns that mounting an NFS file system “can block the host’s I/O completely and prevent it from rebooting”. I would mount the share on the Proxmox host and bind-mount it into the container, as in the file-share row above; the container chapter lists a host NFS mount as a use for bind mounts. That keeps the container unprivileged.
Docker inside a container. An unprivileged container needs keyctl=1 (pct.conf: “This is required to use docker inside a container”) and nesting=1, which the web interface has turned on for new unprivileged containers since Proxmox VE 7.1. On a ZFS host the storage driver is the next question, covered in the Proxmox vs Docker guide. Proxmox VE 9.1 (November 19, 2025) can also create a container straight from an OCI image; running application containers that way is still a technology preview, and Compose support sits on the Roadmap, not in 9.2. I would put Kubernetes in a VM.
Memory ballooning. Proxmox adds the balloon device to every VM by default, even at a fixed memory size. That alone reclaims nothing. The host only reclaims memory when you set the VM’s minimum memory below its maximum; the guest then grows toward the maximum while host RAM use stays under the target (80% by default) and gives memory back when the host runs low.
Snapshots depend on the storage. A container’s snapshots need storage with its own snapshot support, such as ZFS, LVM-thin or Ceph RBD (BTRFS is a technology preview). A container’s volumes are raw images, ZFS subvolumes or directories, never qcow2. Proxmox describes ZFS subvolumes as technically bind mounts backed by managed storage, so they can be resized and snapshotted. On directory, NFS, CIFS or plain LVM storage it has none. A VM has more options. It gets those, plus snapshots on file storage (directory, NFS, CIFS) when its disk is qcow2, and on plain LVM through volume chains (a technology preview since Proxmox VE 9.0).
Backup behavior
Containers
vzdump offers stop, suspend and snapshot modes. Snapshot mode suspends the container, takes a temporary storage snapshot and archives it, so it “requires that all backed up volumes are on a storage that supports snapshots”; without that, suspend mode rsyncs the container to a temporary directory first and needs the space for a copy. To a file target the result is a tar archive; to Proxmox Backup Server it is a pxar archive, and the metadata change-detection mode reuses unchanged files from the previous backup. Bind and device mounts “are never backed up”. A vzdump archive and pct restore are also one way to move a container to a different host without a cluster.
VMs
Snapshot mode is a live backup that copies blocks while the VM runs and works on any storage type, snapshot support or not; with the guest agent enabled and running, it calls guest-fsfreeze-freeze and guest-fsfreeze-thaw to improve consistency. To Proxmox Backup Server the disks go up as fixed-size chunks, and dirty bitmaps can let Proxmox VE upload only the chunks that changed.
Running both for one job
I would keep the ZFS pool on the Proxmox host and bind-mount datasets into unprivileged containers for native Linux services such as Jellyfin or a Samba share. Docker Compose stacks go in a VM, as in the table. The TrueNAS alternative runs the pool in a VM with its disk controller passed through and shares it over NFS or SMB; the host mounts the share and bind-mounts it the same way — so the VM has to be up before anything mounts it. Either way vzdump skips the bind-mounted data. The pool needs its own backup.
Sources
Read 2026-09-26.
- Proxmox VE 9.2.12 manual, dated 2026-09-18: Proxmox Container Toolkit, pct.conf(5), pct(1), QEMU/KVM virtual machines, qm(1), Backup and restore, Storage
- Proxmox VE Roadmap, last edited 2026-09-23: release dates, kernel 7.0 in 9.2, OCI images in 9.1, container device passthrough in 8.1 and 8.2, nesting default in 7.1, volume-chain snapshots in 9.0
- pve-manager source: container Features dialog, Create VM and Create CT buttons, Create CT wizard
- Proxmox Backup Server technical overview
- Home Assistant installation
- Jellyfin hardware acceleration: Intel, NVIDIA
- Plex hardware-accelerated streaming
- TrueNAS, “Yes, You Can (Still) Virtualize TrueNAS”, published 2023-04-03, page metadata modified 2026-07-02
- Netgate: Virtualizing with Proxmox VE, Why FreeBSD?; OPNsense documentation
- WireGuard installation; Kernelnewbies, Linux 5.6
The Power & Cost Calculator prices a host that runs 24/7, and the UPS runtime calculator sizes the battery behind it.
Frequently asked questions
Is a CT the same as an LXC container in Proxmox?
pct command (the Proxmox Container Toolkit) both manage LXC. A VM is created with Create VM and managed with qm.Can I run Docker inside an LXC container in Proxmox?
nesting=1 and keyctl=1 in its features; pct.conf says keyctl is required to use Docker inside a container, and the web interface already turns nesting on for new unprivileged containers. Proxmox’s container chapter still lists nesting application containers inside a QEMU VM as a recommended practice when you need maximum isolation and live migration, and that is where I would put a Compose stack.Is LXC less secure than a VM?
Can I convert an LXC to a VM or vice versa?
What’s the RAM overhead difference?
Does Proxmox Backup Server work with both?
Sources and corrections
- Last updated
- Methodology
- See our methodology for research and review standards.
- Update log
- 2026-09-28 — The page carries the overhead table from my Proxmox VE 8.x test box and my own LXC and VM split, between ‘Where each one wins’ and the decision tree.
- 2026-09-26 — Added the per-service table, each row tied to a Proxmox or vendor document. Corrected the NFS, Docker, snapshot, backup, ballooning and WireGuard statements against the Proxmox VE 9.2.12 manual (dated 2026-09-18), pve-manager’s container Features dialog, the Proxmox Backup Server technical overview and wireguard.com, all read 2026-09-26.
- 2026-08-23 — Dated the benchmark platform. The overhead table was introduced with ‘running Proxmox VE 8.x’ and no indication of when that was current. Proxmox’s FAQ lifecycle table (checked 2026-08-23) ends VE 8 support in 2026-08, so the sentence now says the numbers are 8.x-era and that the structural conclusion, not the absolute figures, is what carries forward. The numbers themselves are unchanged: they are first-party measurements and re-running them is a hardware job, not an edit.
- 2026-08-22 — On 2026-08-22 I removed a non-existent option. The kernel-modules paragraph said modules could be loaded ‘with allow_dangerous_writes’; pct.conf(5) lists the container features as force_rw_sys, fuse, keyctl, mknod, mount and nesting, and the string allow_dangerous_writes does not appear in the Proxmox docs. I rewrote it to say there is no such flag and a container runs on the host kernel’s modules. The ‘Proxmox VE 8.x’ framing of the benchmark table was left as a dated measurement; the current release is 9.2 (21 May 2026).
- Corrections
- Spotted an error or a stale number? Email contact@techfuelhq.com. Confirmed corrections are recorded here with the date and change.