DEV Community

Resul Öztaş
Resul Öztaş

Posted on

Running Docker on Proxmox: LXC vs VMs and the Firewall Rules That Actually Matter

A few months ago I pulled an old i7-7700 box out of a closet, threw 15GB of RAM at it, and turned it into my home lab. No GPU, nothing fancy. I run Proxmox VE on it and use it for agent workloads and internal automation while my actual client sites live on Hetzner. What surprised me was how many small decisions on this box ended up mattering more than the hardware itself — mainly where I put Docker and how I locked the network down.

If you're setting up something similar, the first fork in the road is whether Docker goes inside an LXC container or inside a full VM. People treat this as a minor detail. It isn't.

LXC vs VM is really a question of what you're willing to isolate

An LXC container shares the host kernel. That's the whole trade. You get near-native performance, boot times measured in seconds, and memory overhead that's basically nothing compared to a VM — because there's no second kernel, no virtualized hardware layer, no hypervisor translating every syscall. For a home lab with 15GB total RAM, this matters a lot. Three or four VMs and you've burned half your memory on OS overhead before a single application starts.

A VM, on the other hand, gets its own kernel and a real boundary enforced by hardware virtualization extensions (VT-x/AMD-V), not just kernel namespaces. If something inside breaks out, it's breaking out of an actual virtual machine, not just a namespace boundary that a kernel exploit could theoretically punch through.

So the practical rule I landed on: anything that's mine, that I trust, that just needs to run efficiently — internal tools, automation scripts, my agent pipelines — goes in LXC. Anything that touches untrusted input directly, or that I'd rather not think about too hard from a security perspective, gets a VM. It's not paranoia, it's just matching the isolation level to the actual risk.

Docker inside LXC is fine, but only if you do the nesting right

This is where people get stuck. Docker wants to create its own namespaces, cgroups, and network bridges — and by default, an unprivileged LXC container won't let it, because unprivileged containers deliberately restrict what the process inside can do to the kernel.

The fix isn't switching to a privileged container as a first instinct, even though that's the fastest way to make the error go away. Privileged containers run as root on the host's actual UID, which means a container escape is a host compromise, full stop. What you want instead is keeping the container unprivileged and explicitly enabling nesting and keyctl in its configuration, which unlocks just enough kernel features for Docker's own namespacing to work while keeping the container's root mapped to an unprivileged UID on the host. It takes two lines in the container config. Once that's set, Docker runs inside LXC almost exactly like it would on bare metal, and you keep the isolation boundary that unprivileged containers give you.

I only reach for privileged LXC when a workload genuinely needs raw device access — GPU passthrough, certain hardware pass-through scenarios — and even then I try to scope it as tightly as possible.

The firewall is where most home labs quietly fail

Proxmox has a firewall built in at three layers: datacenter, node, and individual VM/CT. Most guides show you how to open a port. Almost none of them explain why the datacenter-level rules matter more than people assume.

The mistake I see constantly — and made myself, early on — is configuring firewall rules per container and calling it done. That works until you have fifteen containers and you're maintaining fifteen slightly different rule sets, and eventually one of them drifts and quietly exposes something it shouldn't. What actually holds up is defining security groups at the datacenter level — one for "internal services, no external access," one for "web-facing, ports 80/443 only," one for "management access from my own IP range only" — and then just assigning containers to the right group. The rules live in one place. When I need to change something, I change it once.

On top of that, Proxmox's firewall works at the bridge level before traffic even reaches a container's own iptables rules, which means you get a real security boundary independent of whatever the container's own OS is doing. I still run fail2ban inside anything exposing SSH, and ufw as a second layer inside containers that face the internet, but the Proxmox firewall is what stops most of the noise before it gets that far. Defense in depth isn't a buzzword here, it's just fewer 3am log entries.

One more thing that trips people up: Proxmox's firewall is disabled by default even after you configure rules, at both the datacenter and node level independently. I've seen more than one setup where someone wrote a full rule set, felt done, and never actually flipped the enable switch. Check both toggles.

What this setup actually gets you

None of this is exotic. It's an old desktop CPU, consumer RAM, and Proxmox doing what it does well — letting you run a mix of lightweight LXC containers and properly isolated VMs on hardware that would choke if you tried to run everything as full VMs. The firewall layering is the part that took me longest to get right, mostly because the instinct is to configure it reactively, container by container, instead of designing the security groups first and assigning containers into them.

If you're running something similar and want to compare notes, or if you're curious about the agent pipeline I'm running on top of this box, I write about the infrastructure and web/SEO side of things at resuloztas.com

Top comments (0)