JOIN THE NEWSLETTER
ADVERTISEMENT
reserved 728 x 90
Homelab

Proxmox LXC vs VM: I Spent Three Months on the Wrong One

Proxmox LXC vs VM: a decision table for when each is right, why containers cost a fraction of the RAM and boot in about a second, why multiple containers can share one integrated GPU while a VM cannot share it at all, and the GPU virtualisation dead end that cost me three months.
Title card for a guide comparing Proxmox LXC containers and virtual machines

I built a virtual machine to run a browser. It idled at 98% CPU.

Not under load. Idling, with one page open. I gave it more cores and it took those too. Three host reboots later, each taking down every other service on the box, the kernel log told me my hardware lost that GPU feature years ago.

Three months later I rebuilt it as an LXC container and the problem went away. The bottleneck was never graphics, and the VM had been hiding that from me.

Short version for the skimmers. Proxmox containers vs VM comes down to one question: do you need a different kernel, a different operating system, or hard isolation? If not, use a container. Containers cost a fraction of the RAM, start in about a second, and can share hardware like an integrated GPU that a VM cannot touch without IOMMU. Reach for a VM when you have a specific reason you can name out loud.

KEY FACTS

A container running a small service idles at tens of megabytes. The same thing in a VM costs a gigabyte before the service starts.

Containers share the host kernel, so they boot in about a second and have no virtualised hardware.

Multiple containers can share one integrated GPU. A VM cannot share a GPU at all.

A VM needs virtualisation extensions enabled in BIOS. A container does not.

The right question is not “which is better,” it is “do I need a different kernel.”

The actual decision, in one table

You need Use
Windows, BSD, or any non-Linux OS VM
A different kernel version or custom kernel modules VM
Hard isolation from the host kernel VM
To snapshot and roll back an entire OS state VM (containers can too, less cleanly)
A Linux service: file server, media server, database, web app, game server Container
To share an integrated GPU between two workloads Container, and only a container
To run something on hardware without virtualisation extensions Container
To run a lot of small things on modest RAM Container

Most of a home server is the bottom half of that table. Mine is five services, and exactly one of them genuinely needs to be a VM, because it runs Windows.

Why containers are so much cheaper

A VM emulates a computer. It boots a kernel, initialises virtual hardware, runs a full init system, and reserves its memory allocation whether or not it uses it. That is the price of pretending to be a separate machine, and sometimes it is worth paying.

A container shares the host kernel and runs its processes in isolated namespaces. There is no virtual hardware and no second kernel. It starts about as fast as the service inside it starts.

The practical consequence is what you can afford to run. On a 16GB box, giving four services a virtual machine each means allocating most of your RAM before any of them does work. The same four as containers leaves room for the thing you actually care about.

Real number from my own build, because it is the kind of thing nobody writes down: the desktop VM I mentioned idled at around 631MB just for its desktop environment, before the browser. I had budgeted for about 200MB.

The GPU thing, which decided it for me

This is the difference that gets buried in generic comparisons, and it was the whole story on my machine.

If you want a virtual machine to use your GPU, you pass the physical device through. That needs IOMMU support, it needs to be enabled in BIOS, and the card then belongs to that VM exclusively. Nothing else can use it, including the host.

To slice one GPU between multiple VMs you need mediated device support, which for integrated Intel graphics means GVT-g. That is where I lost my evening. My chip’s IDs look right, the log even confirms the IOMMU is enabled, and then:

i915 0000:00:02.0: [drm] Unsupported device. GVT-g is disabled

Support for my generation had been dropped from modern kernels. It was never going to work. The hardware “supports” it on paper and exposes no usable devices at all.

A container does not need any of this. Because it shares the host kernel, you bind the GPU’s device nodes into it:

dev0: /dev/dri/renderD128,gid=993
dev1: /dev/dri/card1,gid=44

No IOMMU. No BIOS setting. No exclusive claim. And critically, you can do this for two containers at once and both will use the same GPU. My media server and my browser container share one integrated GPU today. There is no VM configuration that does that.

I wrote up that device-sharing setup on its own, including where those two group numbers actually come from, in Proxmox iGPU passthrough to an unprivileged LXC.

The part where I was confidently wrong twice

Worth telling, because both mistakes are the same shape.

Mistake one. For months, my notes, my project docs, even a generated reference file all said my CPU lacked the virtualisation extension needed for passthrough. I repeated it enough times that it became a fact I stopped checking. It was wrong. The non-K variant of that chip does support it; the K variant is the one that does not. It was simply switched off in BIOS. A firmware toggle, not a silicon limit.

The punchline is that the excuse was both wrong and irrelevant. Even with it enabled, passing through that integrated GPU still was not possible, and the container route needed none of it.

Mistake two. I added kernel boot parameters to a config file that my system does not read, because it boots with a different bootloader than the one that file belongs to. The file looked right. Two more reboots burned before I checked which bootloader was actually in use.

Both are the same failure: I verified that a thing looked correct instead of verifying that it took effect. That is the lesson I keep relearning and it applies well beyond this topic.

What the problem actually was

Here is the ending, and it is a little humbling.

The browser was slow because of the remote desktop protocol I was using to view it, not because of the graphics rendering. Once I streamed it as a hardware-encoded video stream instead, it was fine. The GPU is now used only to encode that stream. The browser’s rendering is still done in software on the CPU, exactly as before, and nobody can tell.

I had priced a graphics card. I never bought it, because the GPU was never the bottleneck. Three months of assuming the visible expensive symptom was the cause.

Ask “what is actually slow here, and how would I prove it” before “how do I make this component faster.” I put that instruction into the standing prompt I use for server work now, and it has saved me twice since.

When you genuinely do want a VM

I do not want to talk anyone out of VMs. They are the right answer for:

  • Anything not Linux. Windows, BSD, a NAS appliance OS. No debate.
  • Kernel-level work. Custom modules, a different kernel version, anything that loads drivers.
  • Real isolation. If you are running something you actively distrust, the container’s shared kernel is a larger attack surface than a VM’s virtual hardware.
  • Whole-system rollback. VM snapshots that capture an entire OS state are cleaner, and if your workflow is “snapshot, break it, revert,” that is worth the overhead. My malware scanning box is a VM for exactly this reason, and it resets to a known-clean baseline.

That last one is a good example of the reasoning working. It is a VM because I want it isolated from the host kernel and I want to roll the whole thing back. Both reasons I can say out loud.

FAQ

Is LXC faster than a VM? For most home server work, effectively yes, because it is not doing the extra work. There is no virtualised hardware layer and no second kernel. For raw compute inside an already-running guest the gap is small, but startup, memory footprint, and hardware access are all dramatically better.

Can I run Docker inside an LXC container? Yes, with nesting enabled on the container. It works and plenty of people do it. Some prefer a dedicated VM for Docker to keep the layers of abstraction from stacking up.

Are unprivileged containers secure enough? For services you control on a home network, yes. Root inside is an unprivileged high-numbered user outside, so an escape does not hand anyone your host. For anything genuinely hostile, use a VM.

Can I convert a VM to a container? Not directly in any pleasant way. In practice you rebuild the service in a container and move the data, which is usually faster than fighting a conversion. Mine took an evening.

Why is my container’s memory usage lower than what I allocated? Because containers do not reserve their allocation the way a VM does. The number is a ceiling, not a claim. That is a large part of why you can run so many more of them.

Do containers survive a host reboot? Only if you set them to. onboot: 1 is not the default, and finding out the hard way is a rite of passage.

The rule I use now

Container by default. VM when I can name the reason. If the reason is “the container thing sounded hard,” that is not a reason, that is an afternoon.

The full build this all sits on is in how to set up a Proxmox home server, the GPU sharing detail is in Plex hardware transcoding, and the container permissions that trip everyone up are in unprivileged LXC permissions.

MP
Written by Marcos Ponce
I build things and write down what actually happened: AI automation, micro-cap deep dives, 3D printed PC cases, and the occasional song.
ADVERTISEMENT
reserved 336 x 280

One useful build a week.

The next automation, deep dive, or 3D print, written up the way I wish someone had written it for me.

SUBSCRIBE