If you spend any time in Proxmox communities, you'll see this debate come up weekly: "Should I run Docker in an LXC container or in a VM?"
The VM purists will tell you LXC + Docker is cursed. The pragmatists will tell you it works fine. After running this setup for over a year on my homelab, here's my take.
The Short Answer
For homelabs and small deployments: LXC + Docker works great.
For production with untrusted workloads: use VMs.
Why Run Docker in LXC?
1. Resource Efficiency
An LXC container has virtually no overhead. It's just process isolation using Linux namespaces and cgroups — the same technology Docker uses internally.
A typical VM setup:
- VM overhead: 512MB-1GB RAM just for the guest OS
- Disk overhead: 2-5GB for the base system
- CPU overhead: hypervisor context switching
An LXC container:
- RAM overhead: ~50MB
- Disk overhead: ~200MB
- CPU overhead: negligible (native execution)
When you're running 10+ services on a homelab server with 32GB RAM, that overhead adds up.
2. Simpler Backup and Migration
Proxmox can snapshot LXC containers instantly. No guest agent needed, no quiescing issues. The backup is just a tarball of the filesystem.
# Backup an LXC container
vzdump 100 --compress zstd --mode snapshot
# Restore it
pct restore 101 /var/lib/vz/dump/vzdump-lxc-100-*.tar.zst
Compare that to VM backups where you need guest agents, proper shutdown sequences, and larger backup files.
3. Direct Hardware Access
Need to pass through a USB device or access host storage? LXC makes it trivial:
# Mount a host directory
pct set 100 -mp0 /mnt/data,mp=/data
# Pass through a device
lxc.cgroup2.devices.allow: c 189:* rwm
lxc.mount.entry: /dev/bus/usb dev/bus/usb none bind,optional,create=dir
No IOMMU groups, no PCI passthrough complexity.
The Setup
Creating a Docker-Ready LXC Container
# Create the container (Debian 12)
pct create 100 local:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst \
--hostname docker-host \
--memory 4096 \
--cores 2 \
--rootfs local-lvm:20 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--features nesting=1,keyctl=1 \
--unprivileged 1
# Start it
pct start 100
The key flags:
-
nesting=1: Required for Docker to create its own namespaces -
keyctl=1: Needed for some container operations -
unprivileged 1: Security best practice
Installing Docker
# Enter the container
pct enter 100
# Install Docker (official method)
curl -fsSL https://get.docker.com | sh
# Verify it works
docker run hello-world
That's it. No special configuration needed.
Handling AppArmor Issues
Some Docker images fail with AppArmor errors in LXC. The fix:
# On the Proxmox host, edit the container config
nano /etc/pve/lxc/100.conf
# Add this line
lxc.apparmor.profile: unconfined
Then restart the container. This is safe for homelabs; in production you'd write proper AppArmor profiles.
What About Security?
The main argument against Docker-in-LXC is security. Let's break it down:
The risk: If someone escapes the Docker container, they're in an LXC container, not a full VM. LXC provides less isolation than a hypervisor.
The reality for homelabs:
- You control what containers you run
- You're not running untrusted code
- The attack surface is your own services
When to use VMs instead:
- Running untrusted workloads
- Multi-tenant environments
- Compliance requirements
- When you need different kernels
For a homelab running Jellyfin, Home Assistant, and some self-hosted apps? LXC + Docker is perfectly fine.
Performance Comparison
I ran some basic benchmarks comparing the same Docker Compose stack (PostgreSQL + Redis + a Node.js app) in three configurations:
| Setup | Memory Used | Startup Time | Disk I/O |
|---|---|---|---|
| Bare metal Docker | 1.2 GB | 8s | Baseline |
| LXC + Docker | 1.3 GB | 9s | ~Same |
| VM + Docker | 2.1 GB | 45s | -15% |
The LXC overhead is negligible. The VM overhead is noticeable.
Tips From Production Use
1. Use Docker Compose, not Portainer
Portainer adds complexity. A docker-compose.yml file is version-controlled, reproducible, and doesn't need a web UI running 24/7.
2. One service per LXC container
Don't cram everything into one container. Use separate LXC containers for:
- Media stack (Jellyfin, *arr apps)
- Home automation (Home Assistant)
- Development (databases, test apps)
This way you can snapshot, migrate, and resource-limit each independently.
3. Set resource limits
# Limit memory and CPU
pct set 100 --memory 4096 --cores 2
# Set swap limit
pct set 100 --swap 512
Without limits, a runaway container can affect your entire host.
4. Use ZFS or LVM thin provisioning
Snapshots before updates are lifesavers:
# Before updating containers
pct snapshot 100 before-update
# If something breaks
pct rollback 100 before-update
The Bottom Line
Docker in LXC isn't a hack or an anti-pattern. It's a pragmatic choice that trades some isolation for significant resource savings.
For homelabs: go for it.
For production with compliance requirements or untrusted workloads: use VMs.
The Proxmox forums will keep debating this forever, but thousands of homelabs run this setup without issues. Try it yourself and see.
What's your Proxmox setup? Running Docker in LXC, VMs, or bare metal? Drop a comment below.
Top comments (0)