5.6 KiB
Proxmox LXC arr-stack Deployment
This walkthrough deploys the arr stack inside a Docker LXC. While unprivileged is the security default, this guide assumes a Privileged LXC if you intend to use NVIDIA GPU acceleration for Emby.
- app configs and SQLite databases on local LXC disk at
/opt/arr-stack/appdata; - downloads and media on a UniFi UNAS Pro NFS share;
- NFS mounted only on the Proxmox host;
- the NFS-backed directory bind-mounted into the LXC at
/mnt/unas/arr_data; - the full shared root visible to Radarr and Sonarr at
/data.
Privileged vs Unprivileged
- Privileged (Recommended for GPU): Required for seamless NVIDIA driver access and CUDA compute. Root inside the LXC maps directly to root on the host.
- Unprivileged (Standard Security): Safer, but requires complex UID mapping for GPU and NFS write access.
The storage path is:
UNAS NFS export
-> Proxmox host: /mnt/unas/arr_data
-> LXC: /mnt/unas/arr_data
-> Docker containers: /data
Do not start the Compose stack until the storage verification steps pass.
Values Used in This Guide
Replace these examples with your real values:
| Name | Example | Meaning |
|---|---|---|
UNAS_IP |
10.0.10.20 |
UNAS Pro IP address |
PVE_IP |
10.0.10.10 |
Proxmox host IP allowed by the UNAS |
VMID |
120 |
Docker LXC container ID |
NFS_EXPORT |
/volume1/.srv/.unifi-drive/Media/.data |
Real export from showmount |
HOST_DATA |
/mnt/unas/arr_data |
NFS mountpoint on Proxmox |
LXC_DATA |
/mnt/unas/arr_data |
Bind-mount destination inside the LXC |
CONFIG_ROOT |
/opt/arr-stack/appdata |
Local LXC config path |
PUID:PGID |
1000:1000 |
Identity used by the containers |
Run Proxmox commands from the Proxmox shell as root. Run LXC commands after
entering the LXC with pct enter 120.
1. Enable NFS on the UNAS
Why: the Proxmox host needs permission to mount the shared drive. The LXC must not mount NFS itself.
- Open the UniFi Drive web interface for the UNAS.
- Create or select the shared drive that will contain the arr data, for example
Media. - Enable NFS for that shared drive.
- Add the Proxmox host IP, for example
10.0.10.10, to the NFS allowed clients/hosts list. Do not add only the LXC IP because Proxmox is the NFS client. - Grant that client read/write access.
- Save/apply the NFS settings.
2. Discover the Real NFS Export Path
On the Proxmox host, install the NFS client tools:
apt update
apt install -y nfs-common
Set the UNAS address and list its exports:
UNAS_IP=10.0.10.20
showmount -e "$UNAS_IP"
Copy the exact export path (e.g., /volume1/.srv/.unifi-drive/Media/.data).
3. Mount NFS on the Proxmox Host
Set the values for this shell session:
UNAS_IP=10.0.10.20
NFS_EXPORT=/volume1/.srv/.unifi-drive/Media/.data
HOST_DATA=/mnt/unas/arr_data
Create the empty host mountpoint:
mkdir -p "$HOST_DATA"
Back up and edit /etc/fstab:
nano /etc/fstab
Add one line:
10.0.10.20:/volume1/.srv/.unifi-drive/Media/.data /mnt/unas/arr_data nfs vers=4.1,noatime,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=10s 0 0
Apply and verify:
systemctl daemon-reload
mount -a
findmnt "$HOST_DATA"
4. Bind Mount the Host Path into the LXC
Stop the LXC before changing mountpoints:
VMID=120
pct stop "$VMID"
Confirm the LXC is configured correctly (privileged if using GPU):
pct config "$VMID"
Bind mount the host path:
pct set "$VMID" -mp0 /mnt/unas/arr_data,mp=/mnt/unas/arr_data
Start and enter the LXC:
pct start "$VMID"
pct enter "$VMID"
5. Understand UID/GID Mapping
Why: In a Privileged LXC, mapping is 1:1. UID 1000 inside the LXC is
UID 1000 on the host. In an Unprivileged LXC, IDs are shifted.
This guide assumes container/LXC user 1000:1000.
Inside the LXC, create the user if it does not exist:
groupadd --gid 1000 arrstack
useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash arrstack
Set ownership on the NFS-backed arr data from the Proxmox host:
chown -R 1000:1000 /mnt/unas/arr_data
Verify write access inside the LXC:
su -s /bin/sh -c 'touch /mnt/unas/arr_data/.lxc-write-test && rm /mnt/unas/arr_data/.lxc-write-test' arrstack
6. Prepare Local Config and Shared Data
Create local config directories on the LXC root disk:
CONFIG_ROOT=/opt/arr-stack/appdata
mkdir -p "$CONFIG_ROOT"/{gluetun,qbittorrent,sabnzbd,prowlarr,radarr,sonarr,bazarr,emby,seerr,recyclarr,unpackerr}
chown -R 1000:1000 /opt/arr-stack
Create the TRaSH-style shared directory tree:
su -s /bin/bash -c 'mkdir -p /mnt/unas/arr_data/torrents/{movies,tv} /mnt/unas/arr_data/usenet/{incomplete,complete/{movies,tv}} /mnt/unas/arr_data/media/{movies,tv}' arrstack
touch /mnt/unas/arr_data/.arr-stack-storage
chown 1000:1000 /mnt/unas/arr_data/.arr-stack-storage
7. Configure Paths Inside the Applications
All applications must use paths under the same /data prefix for hardlinks to
work.
| Application | Path |
|---|---|
| qBittorrent | /data/torrents/movies, /data/torrents/tv |
| SABnzbd | /data/usenet/incomplete, /data/usenet/complete |
| Radarr | /data/media/movies |
| Sonarr | /data/media/tv |
| Bazarr / Emby | /data/media/movies, /data/media/tv |
8. Verify Hardlinks
Import a torrent through Radarr or Sonarr, then check the link count:
stat -c '%h %n' /mnt/unas/arr_data/torrents/movies/<file> /mnt/unas/arr_data/media/movies/<file>
A link count of 2 proves the hardlink is working.