Proxmox¶
Everything runs on two Proxmox VE hosts:
- Services host. The NAS, the Docker host and the workstation are VMs, and small services such as the UniFi controller are LXC containers.
- Backup host. Separate hardware with a backup NAS VM, Proxmox Backup Server and the second DNS server (AdGuard Home + Unbound) as LXCs.
Keeping backups on their own machine means a failed disk controller, a bad Proxmox upgrade or a mistake on the services host can't take the backups down with it. Backups themselves are covered in Files → Backups.
VM or LXC?¶
| VM | LXC container | |
|---|---|---|
| Kernel | Its own | Shares the host's |
| Overhead | A few GB of RAM, full OS | Tiny: boots in seconds, ~100 MB of RAM |
| Hardware | Full PCIe passthrough (GPU, disk controllers) | Bind mounts and device nodes only |
| Docker inside | Just works | Possible (nesting=1) but fiddly |
| Use it for | Anything with passthrough hardware, Docker hosts, other OSes (Home Assistant OS) | Single small services: the UniFi controller, PBS, AdGuard Home, Unbound |
My rule: one service and no special hardware → LXC; everything else → VM.
For LXCs, the Proxmox VE Helper-Scripts are a quick start. They're one-line installers that create a ready-made, unprivileged container for a given app. Read the script before piping it into a root shell.
GPU passthrough¶
The Docker host VM gets the whole NVIDIA GPU, which is used by Immich, Frigate, Whisper and Ollama. The steps on an AMD host:
-
Turn on IOMMU (also called AMD-Vi or VT-d) in the BIOS, then on the kernel command line. Proxmox with LVM boots via GRUB: edit
/etc/default/grub, runupdate-gruband reboot.For Intel, use
intel_iommu=on.iommu=ptonly maps devices that are passed through, which is faster for the host. -
Load VFIO and keep the host's own drivers off the GPU:
Then
update-initramfs -u -k alland reboot. -
Check the IOMMU groups. A device can only be passed through together with everything else in its group:
iommu.sh#!/bin/bash for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*} printf 'IOMMU Group %s ' "$n" lspci -nns "${d##*/}" doneThe GPU and its audio function should be alone in a group. If they share it with other devices,
pcie_acs_override=downstreamon the kernel command line splits the groups. It's a workaround that weakens isolation between those devices, so only add it if you need to. My board needs it. -
Configure the VM. Use machine type
q35, OVMF (UEFI) BIOS and CPU typehost. Then add the GPU under Hardware → Add → PCI Device with All Functions and PCI-Express ticked:Turn memory ballooning off (
balloon: 0). A VM with a passed-through device has to keep all its RAM pinned anyway. -
Inside the VM, install the normal NVIDIA driver and the NVIDIA Container Toolkit so Docker containers can use the GPU.
Passing disks through¶
The NAS and the Docker host get whole physical disks. Pass them by their stable /dev/disk/by-id/ name, never /dev/sdX, because those letters can change between boots:
ls -l /dev/disk/by-id/ | grep -v part
qm set <vmid> -scsi1 /dev/disk/by-id/ata-<model>_<serial>,backup=0,iothread=1
backup=0 keeps the disk out of the nightly vzdump job, since its data is backed up file by file with restic instead (see Backups). Add discard=on for SSDs.
The alternative is passing through a whole SATA or HBA controller as a PCI device. The VM then sees the disks directly, including SMART data and spin-down. That's worth doing if a NAS VM needs proper disk health monitoring.
Small things worth doing¶
qemu-guest-agentin every VM (apt install qemu-guest-agent, then tick QEMU Guest Agent under Options). Proxmox can then shut the VM down cleanly, show its IP, and freeze its filesystems for consistent snapshot backups.iothread=1with the VirtIO SCSI single controller gives each disk its own I/O thread.- Start order under Options → Start/Shutdown order: start the NAS first and the Docker host a minute later, so the SMB mounts are there when containers start.