Docker unlocks the enormous ecosystem of self-hosted apps — most projects ship a docker run command or a compose.yaml as their primary install method. But where you run Docker on Proxmox matters more than beginners expect. Get it wrong and a routine host kernel upgrade takes your services down.
This guide covers the real decision — LXC vs VM for Docker — the setup for both, and Portainer for a friendly web UI.
The decision: VM, almost always
Here’s the short version, then the reasoning:
| Docker in a VM (recommended) | Docker in an LXC | |
|---|---|---|
| Kernel | Its own — behaves as documented | Shared with host |
| Upgrades | Clean, isolated | Can break on host kernel upgrade |
| Overhead | ~500 MB–1 GB base RAM | Very low |
| Isolation | Strong | Weak (esp. if privileged) |
| Best for | Your real Docker host | One throwaway lightweight app |
Docker and LXC are both Linux container systems. Running Docker inside LXC nests two layers of namespaces and cgroups that were never meant to stack — it works with the right features enabled, but it’s fragile. A VM gives Docker its own kernel and behaves exactly as every tutorial online assumes. Unless you’re squeezing a single tiny app onto an already-busy node, use a VM.
For one lightweight, stateless app on a RAM-constrained node, a nesting-enabled unprivileged LXC running Docker is fine — as long as you’re happy to rebuild it if a Proxmox upgrade breaks it. For everything else, the VM is worth the extra half-gig of RAM.
Task 1: Build the Docker VM (recommended path)
Create a VM following the first-VM walkthrough: Debian 12, 2 vCPU, 4 GB RAM, 32 GB disk, QEMU guest agent enabled. A cloud-init template makes this instant if you have one.
Inside the VM, use Docker’s official convenience script, then verify:
curl -fsSL https://get.docker.com | sh
docker run --rm hello-world
Add your user to the docker group so you don’t need sudo for every command:
usermod -aG docker youradmin
Log out and back in for the group change to take effect.
Task 2: The LXC path (only if you must)
If you’re deliberately going the LXC route, the container needs the nesting and keyctl features:
pct create 120 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname docker01 \
--cores 2 --memory 2048 --swap 512 \
--rootfs local-lvm:16 \
--unprivileged 1 --features nesting=1,keyctl=1 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--onboot 1 --password --start 1
Then install Docker inside it exactly as in Task 1. See the Proxmox LXC docs on the feature flags.
Docker-in-LXC can break after a Proxmox host kernel upgrade — storage driver mismatches are the usual culprit. Keep your compose files and volumes backed up so the container is disposable.
Task 3: Add Portainer
Portainer gives you a web UI over your Docker host. Deploy it with a persistent volume:
docker volume create portainer_data
docker run -d \
--name portainer \
--restart=always \
-p 9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
Open https://<docker-host-ip>:9443, create the admin account within a few minutes of first launch (a security timeout), and you’re in. From here you can deploy stacks (compose files) straight from the UI.
Rather than :9443 everywhere, put Portainer and your apps behind a reverse proxy with names from Pi-hole local DNS — e.g. https://portainer.homelab.lan.
What’s next
You’ve got a stable Docker host and a UI to manage it. Now make your services reachable at clean HTTPS URLs with a reverse proxy, and don’t forget to back up the VM — your compose files and volumes live there.
Related posts:
- Proxmox VM vs LXC: When to Use Each — the decision this guide applies to Docker.
- Creating Your First Proxmox VM — build the VM your Docker host runs in.
- Creating Your First Proxmox LXC Container — the LXC path, if you take it.
- What Is Traefik? Reverse Proxies Explained — expose your Docker apps over HTTPS.
- Pi-hole on Proxmox LXC — local DNS names for your containers.
- Proxmox Backup Server — back up the VM holding your stacks.