Docker vs LXC on Proxmox: What I Actually Run in Production
Every time I bring a new Proxmox host online for a client, the same question comes up eventually: "why not just run everything in Docker?" It's a fair question, Docker images are portable, the ecosystem is huge, and most developers already know how to write a Dockerfile. But after years of running mixed workloads, MariaDB Galera clusters, PostgreSQL, Redis, RabbitMQ, plus whatever application stack the client ships, I've settled into a split that I rarely deviate from.
LXC for anything stateful and long-lived
For databases, caches, and message queues, I reach for LXC containers directly on the Proxmox host. An LXC shares the host kernel, so there's no virtualization tax: disk I/O and memory behave almost exactly like they would on bare metal. For a MariaDB Galera node or a Redis instance, that difference shows up directly in benchmark numbers, especially on smaller VPS-class hardware where every bit of overhead matters.
The other reason is operational. These services tend to live for years, and I want to be able to pct enter the container, check iostat, tune innodb_buffer_pool_size, or grab a quick pg_dump without an extra container runtime sitting between me and the filesystem. Snapshots and backups at the Proxmox level (vzdump) just work, and restoring a single LXC is a five-minute job.
Why Docker-in-LXC never makes the list
I tried the Docker-in-LXC route early on, mostly because it looked like the best of both worlds: LXC's lightness plus Docker's packaging. In practice it's two layers of containerization sharing one kernel, and that combination is fragile in ways that only show up at the worst time. You need to enable nesting and loosen the AppArmor profile on the container, which already narrows the security benefit LXC gave you in the first place. Then a kernel update on the host can quietly break the overlay storage driver inside the nested Docker daemon, and you find out when a container won't come back up after a routine pct reboot.
Newer Proxmox releases added native OCI image support inside LXC, which is a genuinely useful middle ground: you can pull a standard image and run it as an LXC without a Docker daemon at all. I've started using that for small, mostly stateless services where I'd otherwise have reached for a tiny Docker container. For anything with a database underneath it, though, I still keep my distance.
Docker, but inside a VM
When a client hands me an app packaged as a Dockerfile or a docker-compose stack, which is most of them these days, that stack goes into its own Proxmox VM, not an LXC. The VM gives Docker a real kernel of its own, so updates to the Proxmox host's kernel don't ripple into the container runtime, and the iptables/nftables rules Docker likes to rewrite stay contained to that VM instead of fighting with the host's firewall config.
Yes, that's an extra layer of virtualization compared to Docker-in-LXC. In exchange, I've never had a late-night page because a host kernel update broke someone's containers. For client work, that trade is worth it every time.
The actual split
If I had to write it as a rule: stateful infrastructure (databases, queues, caches) runs as LXC directly on Proxmox, application containers run as Docker inside a VM, and small stateless utilities increasingly run as native OCI-in-LXC now that the option exists. None of this is dogma, if a workload genuinely needs Kubernetes or a specific orchestration layer, that's a different conversation. But for the small-to-mid-size infrastructure I manage, this split has been the most boring, predictable setup I've found, and boring is exactly what you want from infrastructure.