The Arr stack is a handful of containers that work best when they’re wired together correctly. This guide stands up the whole thing in one Docker Compose file — Prowlarr, Radarr, Sonarr, and a VPN-shielded qBittorrent — with the folder layout that makes imports instant instead of slow.
Run it wherever Docker lives: a NAS, a Docker-on-Proxmox LXC or VM, or a dedicated Linux box. Once it’s up, the automation guide connects it to your media server.
Task 1: Get the folder layout right first
This is the single most important decision, and the most common mistake. Downloads and your media library must live under one shared root, mounted identically into every container. When they do, Radarr and Sonarr import files as instant hardlinks — the file appears in the library immediately, taking no extra disk space. When they’re split into separate mounts, every import is a slow copy that doubles disk usage.
The TRaSH Guides recommended layout uses a single /data root:
/data
├── torrents # download client writes here
│ ├── movies
│ └── tv
└── media # media server reads here
├── movies
└── tv
Every container mounts the parent /data, not the subfolders:
Do NOT mount /downloads and /movies as separate volumes. To the container that’s two filesystems, so imports fall back to copy-and-delete. Mount one shared parent (/data) into every app. Hardlinks and atomic moves require everything on one filesystem — and every app needs read access, while the importing apps need write access.
Create it with correct ownership (use the same UID/GID you’ll give the containers):
sudo mkdir -p /data/torrents/{movies,tv} /data/media/{movies,tv}
sudo chown -R 1000:1000 /data
Task 2: Write the Compose file
This runs Prowlarr, Radarr, and Sonarr on the LAN, with qBittorrent inside a gluetun VPN container so only the torrent traffic is shielded. The images come from LinuxServer.io, whose containers are the community standard for the Arr apps and share a consistent PUID/PGID permissions model. The full file is also available as a copy-paste Arr stack Compose playbook.
services:
gluetun:
image: qmcgaw/gluetun:latest
container_name: gluetun
cap_add: [NET_ADMIN]
devices: [/dev/net/tun:/dev/net/tun]
ports:
- "8080:8080" # qBittorrent WebUI, published by gluetun
environment:
- VPN_SERVICE_PROVIDER=protonvpn
- VPN_TYPE=wireguard
- WIREGUARD_PRIVATE_KEY=YOUR_WIREGUARD_PRIVATE_KEY
- SERVER_COUNTRIES=Netherlands
- VPN_PORT_FORWARDING=on
- FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
network_mode: "service:gluetun" # all traffic through the VPN
environment: [PUID=1000, PGID=1000, TZ=Etc/UTC, WEBUI_PORT=8080]
volumes:
- /opt/qbittorrent:/config
- /data/torrents:/data/torrents # same path as the Arr apps
depends_on: [gluetun]
restart: unless-stopped
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
ports: ["9696:9696"]
environment: [PUID=1000, PGID=1000, TZ=Etc/UTC]
volumes: [/opt/prowlarr:/config]
restart: unless-stopped
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
ports: ["7878:7878"]
environment: [PUID=1000, PGID=1000, TZ=Etc/UTC]
volumes:
- /opt/radarr:/config
- /data:/data # ONE shared root -> hardlinks
restart: unless-stopped
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
ports: ["8989:8989"]
environment: [PUID=1000, PGID=1000, TZ=Etc/UTC]
volumes:
- /opt/sonarr:/config
- /data:/data
restart: unless-stopped
gluetun blocks everything except the VPN by default. qBittorrent needs to talk to Radarr/Sonarr on your LAN, so FIREWALL_OUTBOUND_SUBNETS must match your actual subnet or the Arr apps can’t reach the client. Full details and port-forward sync are in the VPN Torrent Stack playbook.
Bring it up and confirm the VPN is actually shielding traffic:
sudo docker compose up -d
# This MUST print a VPN IP, not your home IP:
sudo docker exec gluetun wget -qO- https://api.ipify.org ; echo
Task 3: Wire the apps together
The containers are running but don’t know about each other yet. Three connections to make, all in the web UIs.
Grab each app’s API key from its Settings → General page. In Prowlarr → Settings → Apps → Add, add Radarr and Sonarr, pasting each API key and the container URL (http://radarr:7878, http://sonarr:8989 — containers reach each other by service name). Prowlarr will now sync indexers to both.
In Prowlarr → Indexers → Add Indexer, add the trackers or Usenet indexers you use. They sync to Radarr and Sonarr automatically — you never touch indexer settings in those apps.
In Radarr → Settings → Download Clients → Add → qBittorrent (and again in Sonarr), point it at the qBittorrent host and port (http://<host-ip>:8080, since qBittorrent’s UI is published by gluetun on the host). Set a category — radarr in Radarr, sonarr in Sonarr — so downloads land in the right subfolder.
Set each app’s root folder to the media subpath — /data/media/movies in Radarr, /data/media/tv in Sonarr. Because the download client writes to /data/torrents/... under the same /data mount, imports become instant hardlinks exactly as intended.
Task 4: Test the pipeline
Add one movie in Radarr, mark it monitored, and hit Search. Watch it flow: Radarr queries Prowlarr’s indexers → picks a release → sends it to qBittorrent → and on completion, imports it into /data/media/movies as a hardlink. If the import is instant and disk usage doesn’t jump, your folder layout is correct.
If a download completes but Radarr/Sonarr won’t import it, it’s almost always permissions — the file is owned by a user the app can’t read or move. Keep the same PUID/PGID (1000 here) across qBittorrent and the Arr apps, and make sure /data is owned by that user.
What’s next
You have a working stack: add a title, it downloads and files itself. The automation guide takes it hands-off — import lists that add titles for you, quality profiles that pick the best release, and a notification that rescans your media server the moment a file lands.
Related posts:
- What Is the Arr Stack? — the concepts behind this deploy
- Automating the Arr Stack — import lists, quality profiles, media-server hookup
- VPN-Shielded Torrent Stack — the download client, with kill-switch and port-forward sync
- Docker on Proxmox: LXC vs VM, Done Right — where to run these containers
- Install Plex Media Server on a NAS or Proxmox LXC — the media server this stack feeds
Sources: TRaSH Guides — Hardlinks and Instant Moves, Servarr Docker Guide.