LXC vs QEMU on Proxmox: containers vs VMs, with real examples
June 25, 2026 · 10 min read
Proxmox VE runs two completely different kinds of guest: LXC containers and
QEMU/KVM virtual machines. They look similar in the UI, both have CPU, RAM,
disk and a NIC, but underneath they could not be more different, and that difference decides
which one you should reach for. This post explains the real distinction, then shows it with
actual pct and qm commands, configs and numbers, not hand-waving.
The one difference that explains everything
A QEMU VM runs its own kernel. Proxmox hands it virtual hardware (a virtual CPU, disk, NIC) via KVM, and inside that the guest boots a full operating system: its own kernel, its own init, its own everything. It thinks it's a real machine.
An LXC container shares the host's kernel. There is no second kernel, no virtual hardware, no boot. It's a set of isolated processes running directly on the Proxmox host, fenced off with Linux namespaces and cgroups so they see their own filesystem, network and process list. A container is closer to "a really well-isolated chroot" than to a virtual machine.
Every practical difference, performance, density, isolation, what you can run, falls out of that single fact.
Creating each, for real
An LXC container from a distro template. Note it boots in about a second because there is no kernel to start:
# Container: from a downloaded CT template
pct create 200 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname ct-web --cores 2 --memory 1024 --swap 512 \
--rootfs local-lvm:8 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--unprivileged 1 --features nesting=1
pct start 200
pct exec 200 -- apt-get update # run a command straight into it
A QEMU VM from a cloud image with cloud-init. It boots a full OS, so expect tens of seconds:
# VM: from a cloud image + cloud-init
qm create 300 --name vm-web --cores 2 --memory 2048 \
--net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci
qm set 300 --scsi0 local-lvm:0,import-from=/var/lib/vz/template/iso/debian-12-cloud.qcow2
qm set 300 --ide2 local-lvm:cloudinit --boot order=scsi0
qm set 300 --ciuser admin --sshkeys ~/.ssh/id_ed25519.pub --ipconfig0 ip=dhcp
qm start 300
Already you can feel it: the container is one command and a template; the VM is a cloud image, an import, a cloud-init drive and a real boot.
Density and overhead
Because a container has no guest kernel and no virtualised hardware, its overhead is tiny. An idle Debian container uses a handful of megabytes of RAM and shares the host's page cache. An idle Debian VM reserves its full assigned RAM up front and runs a whole kernel inside it. On the same host you might pack 4–6× more containers than VMs before running out of memory.
Disk tells the same story. A container's root filesystem is just a dataset on the host: a 400 MB base image. A VM disk is a full image with its own partition table, bootloader and kernel.
What you can, and can't, run
This is where the shared kernel bites. In a container you cannot:
- Run a different kernel or a different OS family: no Windows, no BSD, no custom kernel modules. The container uses the host's kernel, version and all.
- Load kernel modules, change
sysctlvalues the host doesn't allow, or run things that need raw hardware/kernel access. - Cleanly run nested virtualisation. If you want to run KVM inside a guest, you need a VM.
A VM has none of those limits: it's a real machine, so it runs Windows, a custom kernel, Docker, nested KVM, anything. The price is the overhead above.
Docker is the classic gotcha: it can run inside an LXC container, but only an
unprivileged container with nesting=1 and the right keyctl/overlay settings, and it's
fiddly. Inside a VM, Docker just works. If a workload is "run other people's containers," a VM is
usually the calmer choice.
Isolation and security
A VM's boundary is the hypervisor: the guest kernel is fully separated from the host kernel, so a kernel-level exploit in the guest stays in the guest. That's why multi-tenant hosting almost always hands customers VMs, not containers.
A container's boundary is the host kernel's namespaces and cgroups. Unprivileged
containers (the default above, --unprivileged 1) map the container's root to an
unprivileged host UID, which closes most escape paths and is what you should always use. But the
container still shares the host kernel, so a kernel vulnerability is a bigger deal than it is for
a VM. For your own workloads that's fine; for hostile tenants, prefer VMs.
Side by side
| LXC container | QEMU/KVM VM | |
|---|---|---|
| Kernel | Shares the host kernel | Runs its own kernel |
| Boot time | ~1 second | Tens of seconds (full boot) |
| RAM overhead | Megabytes; shares page cache | Reserves assigned RAM + guest kernel |
| Density | Very high | Lower |
| OS choice | Linux only, host kernel | Any OS, incl. Windows/BSD |
| Custom kernel / modules | No | Yes |
| Nested virtualisation | No | Yes |
| Isolation boundary | Namespaces + cgroups | Hypervisor |
| Live migration | Restart-based | Live migration supported |
| Provisioning | CT template + pct | Cloud image + cloud-init + qm |
So which should you use?
Reach for an LXC container when the workload is Linux, you control it, and you want it cheap and fast: internal services, a reverse proxy, a database, a dev box, dozens of small services on one host. Containers are the density play.
Reach for a QEMU VM when you need strong isolation (untrusted or multi-tenant workloads), a non-Linux OS, a custom or different kernel, nested virtualisation, or "just run whatever, like a real server." VMs are the compatibility-and-isolation play.
A healthy Proxmox host usually runs both: VMs for the things that need a real machine, containers for the long tail of small Linux services.
Managing both in NexoVirt
NexoVirt treats LXC and QEMU as first-class guests side by side: the same lists, metrics, console, power controls and lifecycle, with a clear type badge so you always know which is which. Creating either is a wizard: containers provision from a CT template, VMs from a cloud-init OS template, and both land in the same panel. You pick the right tool for each workload; NexoVirt makes running a mix of them painless.