LXC vs QEMU on Proxmox: containers vs VMs, with real examples

June 25, 2026 · 10 min read

LXC vs QEMU on Proxmox: containers vs VMs, with real examples

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 sysctl values 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 containerQEMU/KVM VM
KernelShares the host kernelRuns its own kernel
Boot time~1 secondTens of seconds (full boot)
RAM overheadMegabytes; shares page cacheReserves assigned RAM + guest kernel
DensityVery highLower
OS choiceLinux only, host kernelAny OS, incl. Windows/BSD
Custom kernel / modulesNoYes
Nested virtualisationNoYes
Isolation boundaryNamespaces + cgroupsHypervisor
Live migrationRestart-basedLive migration supported
ProvisioningCT template + pctCloud 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.

Try NexoVirt

One clean panel for every Proxmox host.

Related reading