Docker on Proxmox: VM, native LXC or Docker-in-LXC? The guide to choosing
Three approaches coexist for running containers on Proxmox VE. Two are legitimate, one is a trap. This guide compares performance, security, isolation and use cases to help you choose — without dogma, but with a clear opinion.
Docker on Proxmox: three approaches, one right choice
When managing a Proxmox cluster, the question always comes up eventually: where should Docker run? On a classic VM? Directly in an LXC container? Or — and this is where things get complicated — by nesting Docker inside an LXC?
The temptation is understandable: LXC uses less RAM than a VM, boots in seconds, and shares the host kernel. Why "waste" resources on a full VM when LXC can do everything?
The short answer: because isolation, security and maintainability matter more than saving a few hundred MB of RAM. And because Docker-in-LXC, despite the tutorials floating around, is an anti-pattern that always ends up causing problems in production.
This article reviews the three approaches — KVM VM + Docker, native LXC (without Docker), and Docker-in-LXC with nesting — with concrete comparisons on performance, security, and real-world use cases.
Architecture comparison: the 3 models
Before diving into the details, let's visualize how each approach stacks its layers:
KVM VM + Docker
Recommended for production
Native LXC (no Docker)
Clean for simple services
Docker-in-LXC (nesting)
Anti-pattern — avoid
The fundamental difference: in a KVM VM, Docker runs on its own isolated kernel, exactly like on a dedicated server. In an LXC, everything shares the host kernel — including Docker if you install it there, with all the risks that implies.
Performance
The main argument in favor of LXC is performance. Let's see what the numbers actually show:
| Criterion | KVM VM + Docker | Native LXC | Docker-in-LXC |
|---|---|---|---|
| CPU Overhead | 1–3 % (VT-x) | < 1 % | < 1 % |
| Disk I/O | Native (virtio-scsi) | Native | overlay2 overhead |
| Network | ~Native (virtio-net) | Native (veth) | Possible double NAT |
| Boot time | 15–30 s | 1–3 s | 5–15 s (LXC + Docker daemon) |
| RAM footprint | +256–512 MB (guest kernel) | Minimal | +100–200 MB (Docker daemon) |
In practice: On modern hardware with VT-x/VT-d, the overhead of a KVM VM is negligible for most workloads. The difference is measured in single-digit percentages. The "LXC is faster" argument is true on paper, but rarely decisive in production. The additional RAM for the guest kernel (256–512 MB) is the real cost — an acceptable price for full isolation.
Security and isolation
This is where the differences become critical. Performance is a trade-off, security is a requirement.
| Criterion | KVM VM | Native LXC | Docker-in-LXC |
|---|---|---|---|
| Kernel isolation | Full (dedicated kernel) | Partial (host kernel) | Weak (host kernel + nesting) |
| Attack surface | Minimal (QEMU + virtio) | Medium (host syscalls) | Large (syscalls + nesting) |
| AppArmor | Not required | Active by default | Often disabled |
| Live migration | Yes (hot) | No (shutdown required) | No (shutdown required) |
| Container escape to host | Extremely difficult | Possible (kernel CVE) | Increased risk |
| Multi-tenant | Yes — full isolation | Risky | Dangerous |
KVM VM: hardware isolation
A KVM VM runs its own kernel in a memory space isolated by the processor (Intel VT-x / AMD-V). An exploit inside a Docker container reaches at most the guest kernel — not the host. Additionally, live migration allows moving a VM between cluster nodes with zero downtime, which is impossible with LXC.
Docker-in-LXC: why it's an anti-pattern in production
Docker inside LXC combines the worst of both worlds: the Docker container shares the host kernel via LXC, nesting weakens namespace protections, and the necessary workarounds (disabling AppArmor, extended permissions) significantly expand the attack surface. A Docker exploit can reach all the way up to the Proxmox host — without the guest kernel barrier that a VM provides. For dedicated hardware access (GPU, USB controllers), a VM with PCIe passthrough is usually cleaner than privileged-LXC device mapping.
Docker-in-LXC: why it's problematic
To run Docker inside a Proxmox LXC container, you need to enable nesting — the nesting of Linux namespaces. Here's what that entails in practice:
Required LXC configuration
# /etc/pve/lxc/<CTID>.conf
# Enable nesting (required for Docker)
features: nesting=1,keyctl=1
# Sometimes needed for overlay2
features: nesting=1,keyctl=1,fuse=1
# If it still doesn't work... (danger)
lxc.apparmor.profile: unconfined
lxc.cgroup2.devices.allow: a
lxc.cap.drop:
lxc.mount.auto: proc:rw sys:rwEach line added further weakens the container's isolation. The last four lines essentially disable nearly all LXC security.
"systemd 255 detected. you may need to enable nesting": the quick fix (and the 249 / 252 / 257 variants)
If you see the warning warn: systemd 255 detected. you may need to enable nesting. task warnings: 1 when starting an LXC container, it's not a false alarm — systemd actively probes the cgroup delegation and refuses to start services when it detects an unprivileged environment without nesting.
The number in the message is just the systemd version shipped by the container's distribution — it is not a different bug, and the fix is identical for all of them. Which number you get depends only on the template you started from:
| Warning you see | LXC template |
|---|---|
warn: systemd 249 detected. you may need to enable nesting. | Ubuntu 22.04 LTS (jammy) |
warn: systemd 252 detected. you may need to enable nesting. | Debian 12 (bookworm) |
warn: systemd 255 detected. you may need to enable nesting. | Ubuntu 24.04 LTS (noble) |
warn: systemd 256 detected. you may need to enable nesting. | Ubuntu 24.10 (oracular) |
warn: systemd 257 detected. you may need to enable nesting. | Debian 13 (trixie), Ubuntu 25.04 |
Newer releases will simply print a higher number (258 and beyond): the check has been in the Proxmox LXC start task since systemd began enforcing cgroup v2 delegation, and nothing about the remedy changes.
The two-line fix — same for 249, 252, 255, 256, 257 and later:
# On the Proxmox host:
echo "features: nesting=1,keyctl=1" >> /etc/pve/lxc/<CTID>.conf
pct restart <CTID>If the warning persists after nesting is enabled, the container template predates systemd's cgroup v2 requirements. Recreate the LXC from a current Debian 12/13 or Ubuntu 22.04+ template — older images ship with systemd configurations that can't be patched at runtime.
The root cause, not the symptom: systemd is flagging that the LXC is missing capabilities Docker also needs. Fixing the warning with nesting removes the warning — but it doesn't make Docker-in-LXC safe. It just makes it start. See the Security section above for why that distinction matters.
Nesting = weakened isolation
The nesting=1 flag allows the creation of nested namespaces. Docker then creates sub-namespaces within a space already shared with the host. The boundary between "container" and "host" becomes blurred.
AppArmor often disabled
Docker and LXC's AppArmor regularly conflict. The most common "solution" found on forums: lxc.apparmor.profile: unconfined. This disables Mandatory Access Control — the last line of defense between the container and the host.
Issues with cgroups v2 and overlay2
Proxmox has used cgroups v2 by default since version 7. Some Docker-in-LXC configurations require additional workarounds for the overlay2 storage driver, or even a fallback to vfs — which is significantly less performant.
keyctl and FUSE: expanded permissions
Enabling keyctl=1 and fuse=1 gives the LXC container access to syscalls that are normally restricted. Each additional permission is a potential vector for privilege escalation to the host.
The end result: to run Docker in an LXC, you progressively disable the protections that justify LXC's existence. You end up with a container that is neither as secure as a standard LXC, nor as isolated as a VM — the worst of both worlds.
Native LXC: when it's the right choice
To be clear: the problem isn't LXC itself — it's Docker inside LXC. Used natively, a Proxmox LXC container is a legitimate tool for certain use cases:
LXC shines for simple services
- Reverse proxy (nginx, HAProxy) — lightweight, no Docker needed
- Recursive DNS (Unbound, Pi-hole) — single service, system configuration
- Small static web server — Apache/nginx serving files
- Lightweight monitoring — collection agents, Prometheus exporters
- System tools — VPN, SSH bastion, jumpbox
Native LXC limitations
As soon as you need docker-compose, a CI/CD pipeline, OCI images or a container orchestration ecosystem, native LXC reaches its limits. There's no docker pull, no Dockerfile, no Docker volumes. For these use cases, a VM with Docker is the right answer. And an important reminder: live migration is not available with LXC — moving between nodes requires stopping and restarting the container.
Use cases: when to choose what
| Scenario | Recommendation |
|---|---|
| Production with Docker | KVM VM + Docker — isolation, live migration, standard |
| Microservices / docker-compose | KVM VM + Docker — full ecosystem |
| CI/CD (GitLab Runner, Jenkins) | KVM VM + Docker — isolated builds, native DinD |
| Multi-tenant / isolated clients | KVM VM — full isolation mandatory |
| Simple service (DNS, proxy, static web) | Native LXC — lightweight and clean, no Docker |
| Quick dev/test with Docker | Docker-in-LXC acceptable — if disposable and not exposed |
| Legacy application without Docker | Native LXC or VM depending on required isolation |
The honest verdict
VM + Docker
The production choice
- Full isolation (dedicated kernel)
- Standard Docker, no hacks
- Live migration between nodes
- Snapshots and PBS backup
Native LXC
Clean for simple tasks
- Instant boot
- Minimal resource usage
- No Docker / no OCI
- No live migration
Docker-in-LXC
The trap to avoid
- Weakened security (nesting)
- Fragile workarounds
- False RAM savings
- No live migration
Our approach at RDEM Systems
At RDEM Systems, we made a clear choice: 100% KVM VMs, no LXC in our production infrastructure. Here's why.
Why 100% VMs at RDEM Systems
Full client isolation
Each client has dedicated VMs with their own kernel. No kernel sharing between tenants — a compromise in one client's environment cannot affect the others.
Docker works without hacks
docker-compose, volumes, overlay2, multi-stage builders — everything works natively in a VM, without nesting, without workarounds, without surprises after a Proxmox update.
Live migration between hypervisors
We move VMs between cluster nodes with zero downtime — for maintenance, load balancing or hypervisor updates. This is impossible with LXC, which requires stopping and restarting.
Clean snapshots and PBS backups
VMs benefit from consistent snapshots (with qemu-guest-agent for fsfreeze) and PBS backups with deduplication — our 3-2-1 backup strategy relies entirely on VMs.
The cost: yes, each VM consumes 256–512 MB more RAM than an equivalent LXC for the guest kernel. On a cluster with hundreds of GB of RAM, this is a marginal price for full isolation, live migration and worry-free maintenance.
Discover our managed Proxmox VPS and VDS : all our production VMs are deployed with Docker on KVM, with PBS backup and live migration included.
Best practices
Docker on Proxmox VM checklist
- Install qemu-guest-agent in each VM: allows Proxmox to communicate with the VM (fsfreeze for snapshots, clean shutdown)
- Separate Docker storage: dedicate a virtio-scsi disk to the
/var/lib/dockerdirectory — avoids saturating the rootfs and simplifies sizing - cgroups v2 enabled: Proxmox and Docker natively support cgroups v2 — don't switch to v1 to work around an LXC issue
- Limit resources: configure CPU and RAM limits in Proxmox and in Docker constraints (deploy.resources)
- Regular PBS backups: schedule daily backups with verification — Docker containers are ephemeral, but volumes are not. See the PBS best-practices guide (verify, 3-2-1-1-0)
- Bridge or VLAN networking: use Proxmox bridges or VLANs rather than Docker's default NAT for clean, performant networking
- Docker + VM monitoring: track the essential KPIs at both levels — a swapping VM or an OOM Docker daemon won't be visible at the Proxmox level alone
Frequently asked questions
Official documentation
To dive deeper into the concepts covered in this article:
Related articles
Our 3-2-1 backup strategy
PBS, deduplication, verify jobs and ransomware protection across 4 datacenters.
Public NTP on Proxmox VM
Clock drift, Chrony, VM vs bare-metal: RDEM Systems field report.
Migrate from VMware to Proxmox
Complete guide: methodology, tools and pitfalls to avoid.
Proxmox 8 to 9 Migration
Upgrade path, Debian 13/Trixie, breaking changes and rollback plan.
Managed Proxmox by RDEM Systems
Monitoring, backups, updates — we manage your infrastructure.
Need a well-architected Docker on Proxmox setup?
We deploy and manage Proxmox clusters with Docker on VMs — full isolation, live migration, PBS backups. No LXC, no hacks, no surprises.