Introduction – From Raspberry Pi to Full‑Blown Proxmox
When I first dipped my toes into self‑hosting, a modest Raspberry Pi and a handful of Docker containers were enough to keep a dozen open‑source tools running. The lightweight nature of Docker meant the Pi could juggle a personal wiki, a password manager, a media server and still have headroom for tinkering.
Encouraged by that success, I upgraded to a dedicated Proxmox Virtual Environment (PVE) box, expecting to simply copy‑paste my Docker workflow into LXC containers. The reality was a little harsher – LXC is not Docker, and treating them as interchangeable quickly led to broken services, networking glitches and a server that felt more fragile than the Pi it replaced.
This guide pulls together the lessons I learned, explains the technical differences that matter, and offers a step‑by‑step recipe for getting a Proxmox host to behave like a reliable, low‑maintenance self‑hosting platform.
1. LXC vs Docker – The Core Differences
| Feature | Docker | LXC |
|---|---|---|
| Isolation model | Uses a daemon to manage images and runs each container with its own user‑space, but shares the kernel. | Provides a full‑system container that behaves like a lightweight VM – it runs its own init system, can have its own systemd, and more closely mirrors a traditional Linux distro. |
| Image handling | Pulls layered images from Docker Hub or private registries; containers are usually immutable. | Relies on OS templates (e.g., debian-12-standard) that are unpacked on first boot; changes are persisted directly in the container’s rootfs. |
| Networking | Typically uses a bridge (docker0) with NAT; port mapping is explicit (-p). |
Can use a bridge, macvlan, or even a routed setup; each container can have its own IP address on the LAN, which is handy for services that expect a real host. |
| Management tools | docker run, docker compose, Docker Desktop – all centred around the Docker daemon. |
Managed through pct commands or the Proxmox web UI; integrates with Proxmox’s backup, snapshot and migration features. |
Understanding these distinctions is the first step to avoiding Docker‑style missteps in an LXC world.
2. Why Treating LXC Like Docker Breaks Things
2.1 Image Pulls vs. Templates
Docker users are accustomed to docker pull nginx:latest and then instantly running a container. In Proxmox, pulling a Docker image into an LXC container does not give you a clean base system – you end up with a hybrid that lacks the expected init system, causing services that rely on systemd to fail.
2.2 Port Mapping Confusion
Docker’s -p 8080:80 syntax creates a NAT rule on the host. LXC containers, however, often sit on a bridge with their own IP address. Trying to map ports the Docker way can lead to duplicate NAT entries or, worse, containers that are unreachable from the LAN.
2.3 Volume Mounts and Permissions
Docker’s bind‑mounts (-v /host/path:/container/path) are straightforward because the Docker daemon runs as root. LXC containers have their own UID/GID mapping; a naïve bind‑mount can result in permission errors that are hard to debug.
3. Setting Up Proxmox the Right Way
3.1 Choose the Correct Template
Proxmox ships with a library of ready‑made templates. For most self‑hosted services, the debian‑12‑standard or ubuntu‑22‑04‑cloud templates are solid choices. Create a new LXC via the web UI, select the template, and allocate a modest amount of RAM (e.g., 512 MiB) and CPU (1‑2 cores) – you can always adjust later.
3.2 Networking – Bridge vs. MacVLAN
- Bridge (default): All containers share a virtual bridge (
vmbr0). Assign each container a static IP from your home subnet (e.g.,192.168.1.150). This makes services like Nextcloud or Home Assistant appear as first‑class devices on your network. - MacVLAN: If you need containers to be on the same broadcast domain as physical devices without a bridge, enable macvlan on the host NIC and give each container its own MAC address. Note that macvlan containers cannot communicate with the host directly – you’ll need a separate bridge for host‑to‑container traffic.
3.3 Persistent Storage
Instead of Docker‑style bind‑mounts, use Proxmox’s storage pools (e.g., local-lvm or a ZFS dataset). Create a dedicated mount point for each service (/var/lib/nextcloud, /opt/portainer) and add it to the container’s configuration via the UI or pct set <CTID> -mp0 /mnt/pool/nextcloud,mp=/var/lib/nextcloud.
3.4 Backups and Snapshots
One of Proxmox’s biggest advantages over Docker is built‑in backup support. Schedule nightly LXC backups (pct backup <CTID>) to a separate storage pool. Snapshots are instantaneous and can be rolled back if an update breaks a service – a safety net Docker users often miss.
4. Practical Example – Migrating a Docker‑Based Home Assistant
- Export your Docker compose file – keep it as a reference.
- Create an LXC using the Ubuntu‑22‑04‑cloud template, allocate 2 GiB RAM and 2 CPU cores.
- Assign a static IP (e.g.,
192.168.1.200) on the bridge. - Mount a ZFS dataset for Home Assistant’s config directory.
- Install dependencies inside the container:
apt update && apt install -y python3 python3-venv python3-pip libffi-dev libssl-dev - Create a virtual environment and install Home Assistant via pip – this mirrors the Docker image but runs under the container’s own init system.
- Enable the service with a systemd unit so the container boots straight into Home Assistant.
- Test connectivity from a phone or tablet; the static IP means you can add the instance to the Android Home app without extra port forwarding.
- Schedule a weekly backup in Proxmox to protect your automations.
The result is a self‑hosted hub that feels as snappy as the original Docker setup, but with the added safety of Proxmox snapshots and the ability to run other services side‑by‑side without Docker’s daemon overhead.
5. Tips for Keeping a Low‑Power Server Responsive
- Limit RAM per container: Over‑provisioning leads to swapping, which kills performance on a modest CPU.
- Use
pct set <CTID> -limitsto cap CPU usage; Proxmox will enforce the ceiling, preventing a single container from hogging the host. - Turn off unnecessary services inside each LXC (e.g.,
systemd-resolvedif you’re using the host’s DNS). - Monitor with
pveperfand the Proxmox dashboard – keep an eye on I/O latency; a single heavy backup can stall other containers if you’re on a single SSD. - Consider ZFS for storage if you have spare RAM; its copy‑on‑write snapshots pair nicely with Proxmox backups, but remember ZFS likes at least 8 GiB of RAM for optimal performance.
6. When to Still Use Docker Inside an LXC
There are niche cases where Docker’s ecosystem (e.g., specific images that rely on Docker‑specific features) is unavoidable. In those scenarios, spin up a dedicated LXC that runs Docker as its only purpose. Allocate enough resources, enable the nesting=1 flag, and treat that container as a mini‑Docker host – but keep it isolated from your primary services to avoid the confusion described earlier.
7. Bottom Line for the Self‑Hosting Enthusiast
Switching from Docker on a Pi to LXC on Proxmox is a step up in capability, not a simple one‑to‑one migration. By respecting the architectural differences – using proper templates, configuring networking the Proxmox way, and leveraging built‑in backup tools – you turn a hobbyist box into a production‑grade home server.
For Android users who rely on self‑hosted services (e.g., a personal cloud, password vault, or media server), the payoff is clear: faster response times, fewer crashes, and the peace of mind that a single mis‑configured container won’t bring the whole network down.
Takeaway: Treat LXC as a lightweight VM, not a Docker shim, and let Proxmox do the heavy lifting. Your self‑hosted ecosystem will become more robust, and you’ll finally get the most out of that server you invested in.