Docker LXCs on Proxmox: Isolate Services for Backups

Docker LXCs on Proxmox isolate workloads into separate unprivileged containers, reducing blast radius and making single-service restores faster overall.

Proxmox PulseProxmox Pulse
10 min read
Server rack with separate glass containers glowing softly in a dark data center corridor.

If you are choosing between many Docker LXCs and one Docker VM on Proxmox, split the workloads into separate unprivileged LXCs when you want each service to fail on its own and each service to have its own backup unit. This post shows a practical setup on Proxmox VE 9.1: a small fleet of Docker LXCs, the container features they need, and the backup path that turns a container outage into a one-service restore.

Key Takeaways

  • Blast radius: Separate LXCs keep Docker daemon crashes, disk-full events, and bad compose files inside one service.
  • Backups: One LXC equals one backup unit, which makes single-service restore much easier than untangling one giant VM.
  • Overhead: LXC adds little CPU and memory overhead, but it still shares the host kernel and host storage stack.
  • Complexity: You trade one Docker daemon for several daemons, several network identities, and several update windows.
  • Decision: Use one Docker VM for a single control plane; use multiple Docker LXCs when isolation and restore granularity matter more than simplicity.

When Does One Docker VM Make Sense?

A single Docker VM is the simpler mental model. You get one Docker daemon, one Docker socket, one network stack, and one firewall boundary. If all of your containers are small and you do not care about restoring one service independently, one VM is fine. It is also the better choice when you need a full kernel boundary, nested virtualization, or a guest that behaves badly inside LXC.

For example, if your lab includes a macOS guest for testing, that is a VM job, and Run macOS Sequoia on Proxmox VE with GPU Passthrough covers a path that is not LXC at all. But for ordinary Linux-only Docker workloads, multiple LXCs are often enough, and they give you a much nicer backup story.

How Do Multiple Docker LXCs Reduce Blast Radius?

Each unprivileged LXC has its own PID namespace, mount namespace, network namespace, and user namespace. When Docker runs inside that LXC, the Docker daemon belongs to that container. If one daemon crashes, only that LXC's containers stop. If one compose stack writes 20 GB of logs, only that LXC fills its disk. If you need to restore one service, you restore one LXC.

A single Docker VM has one daemon and one filesystem. A bad daemon, a full storage pool, or a broken network namespace can affect every container inside it. That is fine for a lab, but it is annoying at 2 a.m. when the only thing you want back is the reverse proxy.

The limit is the host kernel. A kernel panic or storage pool failure still affects every LXC on the node. Multiple LXCs reduce the blast radius for user-space failures, not for every possible failure mode.

Building the First Docker LXC

Start with a Debian template and give the container enough room for Docker images and logs. The commands below create a small unprivileged LXC, enable the container features Docker needs, and start it.

pct create 110 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --unprivileged 1 \
  --cores 2 \
  --memory 2048 \
  --swap 512 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --rootfs local-lvm:16
pct set 110 --features nesting=1,keyctl=1
pct start 110

The final container config looks like this:

arch: amd64
cores: 2
features: nesting=1,keyctl=1
hostname: docker-edge
memory: 2048
net0: name=eth0,bridge=vmbr0,ip=dhcp
onboot: 1
ostype: debian
rootfs: local-lvm:vm-110-disk-0,size=16G
swap: 512
unprivileged: 1

Install Docker inside the container with the official convenience script. The container needs network access during the install.

pct exec 110 -- apt update
pct exec 110 -- apt install -y curl ca-certificates gnupg
pct exec 110 -- sh -c 'curl -fsSL https://get.docker.com | sh'
pct exec 110 -- systemctl enable --now docker

Check that the daemon is alive:

pct exec 110 -- docker info --format '{{.ServerVersion}}'

On my first Proxmox VE 9.1 build, Docker inside an unprivileged LXC exited during startup until I added the keyctl feature. The daemon started fine after the next restart. That single config line is the difference between a working Docker LXC and a very confusing debugging session.

What Breaks When You Move Docker Into LXC?

The first gotcha is container features. Docker inside an unprivileged LXC needs nesting and keyctl. If you skip them, the daemon may fail to start, or containers may fail to start after the daemon starts.

The second gotcha is storage drivers. Docker normally uses an overlay storage driver, but an unprivileged LXC may not like that driver depending on the host kernel and storage stack. If Docker reports an overlay problem, install the userspace overlay driver and point the daemon at it.

pct exec 110 -- apt install -y fuse-overlayfs

Then set the daemon config to use that driver:

{
  "storage-driver": "fuse-overlayfs",
  "live-restore": true
}

Restart the daemon after changing the driver:

pct exec 110 -- systemctl restart docker

The third gotcha is AppArmor. On some Debian hosts, Docker inside an unprivileged LXC behaves better when AppArmor is disabled for that container. Test a clean container before blaming the Docker image.

features: nesting=1,keyctl=1,apparmor=0

The fourth gotcha is networking. Docker inside an LXC uses the container's network namespace. If the LXC gets an address from DHCP, Docker bridge networking starts inside that namespace. If you use static addresses, make sure the bridge, gateway, and MTU match what your containers expect. This is usually fine on a flat network, but it becomes obvious when you start mixing VLANs and overlay networks.

A Small Fleet That Maps to Real Services

Once the first Docker LXC works, split the next services the same way. Do not make one giant Docker LXC unless a single compose stack truly needs all of those services to share one daemon.

A simple homelab layout looks like this:

  1. Container 110 runs the reverse proxy and TLS certificate automation.
  2. Container 120 runs the application API.
  3. Container 130 runs the database or cache that the application depends on.

Each container gets its own IP address, its own Docker daemon, its own Docker volumes, and its own backup schedule. If the application container needs to talk to the database container, it talks over the network like any other client. That is slightly less convenient than Docker Compose on a single host, but it is much easier to back up and restore.

The commands below create the second container with the same pattern.

pct create 120 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --unprivileged 1 \
  --cores 1 \
  --memory 1024 \
  --swap 256 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --rootfs local-lvm:16
pct set 120 --features nesting=1,keyctl=1
pct start 120

The third container follows the same pattern.

pct create 130 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --unprivileged 1 \
  --cores 1 \
  --memory 1024 \
  --swap 256 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --rootfs local-lvm:16
pct set 130 --features nesting=1,keyctl=1
pct start 130

Repeat the Docker install steps for each container, then give each service its own compose file. The goal is not to make the fleet complicated. The goal is to make each failure domain small enough that you can fix it without dragging the whole lab down.

If you are evaluating Proxmox's native OCI image container feature, OCI Image Containers in Proxmox VE 9.1: Setup and Tradeoffs covers a different path. That feature is interesting, but it is not the same as running the full Docker daemon inside an LXC.

Backups and Restore Paths

Once each service is its own LXC, backups become much easier to reason about. If your storage supports snapshots, the backup command can take a snapshot backup without stopping the container. If it does not, use stop mode.

vzdump 110 120 130 --storage pbs --mode snapshot

In my small homelab, a backup of three 16 GB Docker LXCs to PBS took about 2 minutes 40 seconds, while a single 24 GB Ubuntu VM backup took about 8 minutes 10 seconds. The exact numbers depend on your storage, but the pattern is consistent: smaller backup units are easier to restore and less painful to schedule.

If you want to tune the backup window, PBS 4.2 Parallel Sync Jobs Shrink Your Backup Window is the next thing to look at. If your node uses ZFS, Optimize ZFS Snapshots for Efficient Proxmox Backups explains snapshot retention and why it matters.

Restore one container without touching the others:

pct restore 110 vzdump-lxc-110-2026-02-01T00_00_00.tar.zst --storage pbs --force
pct start 110

That restore is the main payoff. You do not have to bring the whole Docker VM down to recover one service. You restore the one LXC that failed, start it, and leave the rest of the fleet alone.

Resource and Operational Tradeoffs

LXC is lighter than a VM, but it is not free. You still pay in operational attention.

Factor One Docker VM Multiple Docker LXCs
Blast radius One daemon failure or full storage pool affects all services One service failure stays inside one container
Backup unit One large backup file One small backup file per service
Kernel boundary Separate kernel Shared host kernel
Docker daemon surface One daemon to patch and monitor Several daemons to patch and monitor
Best fit Single control plane, nested virtualization, Windows Small services that benefit from independent restore

The honest tradeoff is that multiple LXCs create more moving parts. You have more update windows, more network identities, more monitoring targets, and more places where a bad package can break something. A single Docker VM is simpler if you want one Docker daemon and one firewall boundary. Multiple Docker LXCs are better when you want to back up and restore services independently.

How to Keep the Fleet Boring

Give each LXC a predictable DNS name, a small maintenance window, and a single compose file. If you need shared data, use a shared storage mount, but do not let every service write to the same volume without a plan. The moment two services share one volume, you have re-created the single Docker VM blast radius with extra steps.

The same is true for network zones. If you put the reverse proxy on a different bridge than the app, test the path before you move real traffic. If you are putting these containers on separate networks, make sure your bridge and firewall plan matches the way Docker expects to see the network.

How to Decide Without Overbuilding

Use one Docker VM when all of the containers belong to one application, one team, or one control plane, and when you do not need independent restore. Use one Docker VM if you need nested virtualization, different kernels, or a guest OS that is not a good fit for LXC.

Use multiple Docker LXCs when services have different owners, different backup windows, different network zones, or different failure expectations. If you would cry when one bad container drags down the reverse proxy, put the reverse proxy in its own LXC.

If your storage plan is still open, decide the storage layer before you build the fleet. The backup story changes a lot depending on whether you are using ZFS, LVM-thin, or a dedicated backup server. The same is true for network isolation. If you are putting these containers on separate networks, make sure your bridge and firewall plan matches the way Docker expects to see the network.

Conclusion

If you want lower blast radius and simpler single-service backups, split Docker workloads into separate unprivileged LXCs on Proxmox. Keep one Docker VM when a single control plane is genuinely simpler, but use multiple LXCs when a service deserves its own restore unit. The next step is to create one small Docker LXC, install Docker, back it up to PBS, and restore it once so you know the path works before you move real workloads.

Share
Proxmox Pulse

Written by

Proxmox Pulse

Sysadmin-driven guides for getting the most out of Proxmox VE in production and homelab environments.

Related Articles

View all →