SSD Nodes Learn 8GB RAM — $66/Jahr
Anleitungen Matt ConnorVon Matt Connor · Aktualisiert 2026-08-01

Arr-Stack mit Docker Compose auf einem VPS einrichten

Betreiben Sie Prowlarr, Sonarr, Radarr und qBittorrent in einer Compose-Datei. Gemeinsame PUID-, PGID- und Volume-Pfade erhalten Hardlinks.

Was Sie aufbauen

Ein Docker Compose arr-Stack besteht aus vier Containern, die eine Medienbibliothek verwalten: Prowlarr für Indexer-Einstellungen, Sonarr für Serien, Radarr für Filme und qBittorrent als Download-Client. Sie kommunizieren im Compose-Netzwerk über den Servicenamen miteinander und verwenden denselben Verzeichnisbaum auf dem Host. Die Installation ist kurz. Ob der Stack jahrelang zuverlässig funktioniert oder Sie jede Woche Probleme verursacht, hängt vor allem vom Volume-Layout ab. Deshalb behandelt dieser Leitfaden dieses Thema ausführlich.

Der Stack findet keine Inhalte für Sie. Prowlarr enthält die Indexer, die Sie dort hinzufügen. Welche Indexer Sie verwenden, liegt in Ihrer Verantwortung und unterliegt Ihrer rechtlichen Verantwortung. Dieser Leitfaden behandelt die technische Einrichtung: Benutzer, Pfade, Berechtigungen, Container-Netzwerk und Prüfungen, die den ordnungsgemäßen Betrieb bestätigen.

Wenn Sie noch keine Compose-Datei geschrieben haben, lesen Sie zuerst die Docker-Compose-Grundlagen für einen VPS. In diesem Beitrag wird vorausgesetzt, dass docker compose version auf Ihrem Server bereits eine Ausgabe erzeugt.

Wenn Sonarr einen Download abgeschlossen hat, importiert es die Datei in Ihre Bibliothek. Wenn sich der Download-Ordner und der Bibliotheksordner im selben Dateisystem befinden, ist der Import ein Hardlink: ein zweiter Name, der auf dieselben Daten auf dem Datenträger zeigt. Er benötigt keinen zusätzlichen Speicherplatz und keine zusätzliche Zeit. Der Torrent verteilt weiterhin über den alten Namen, während Ihr Medienserver den neuen Namen liest.

Wenn sich die beiden Ordner in unterschiedlichen Dateisystemen befinden, kann der Kernel diesen Link nicht erstellen. Sonarr wechselt dann zu einer Kopie. Eine 40 GB große Staffel benötigt nun 80 GB Speicherplatz und mehrere Minuten für Ein- und Ausgabe. Außerdem protokolliert das Importprotokoll, dass der Hardlink fehlgeschlagen ist und die Datei stattdessen kopiert wurde. Auf einem VPS mit festgelegtem Speicherplatzlimit ist das der Grund, warum der Speicherplatz innerhalb einer Woche ausgeht.

Hier liegt die Falle. In einem Container ist ein Bind-Mount eine Dateisystemgrenze. Wenn Sie /mnt/data/torrents als /downloads und /mnt/data/media als /tv einbinden, sieht Sonarr zwei separate Mounts und verweigert das Verknüpfen zwischen ihnen, obwohl beide auf demselben Host-Datenträger liegen. Die offizielle Dokumentation des LinuxServer.io-Images sagt dies ausdrücklich: Die Verwendung der separaten Pfade /downloads und /tv verhindert Hardlinks.

Die Lösung ist ein einziger Mount. Jeder Container, der auf Mediendateien zugreift, erhält dasselbe einzelne Volume, /mnt/data:/data, und jeder verwendete Pfad ist ein Ordner innerhalb dieses Volumes. Ein Mount-Punkt, ein Dateisystem, funktionierende Hardlinks.

Benutzer, Gruppe und Verzeichnisse erstellen

Die Container schreiben Dateien mit einer numerischen Benutzer-ID, die durch PUID und PGID festgelegt wird. Verwenden Sie Ihr eigenes Konto, damit Sie diese Dateien über SSH ohne sudo lesen und bearbeiten können.

id -u
id -g

Beide Befehle geben auf einem frischen Ubuntu VPS normalerweise 1000 aus. Erstellen Sie nun die Verzeichnisstruktur. Legen Sie sie auf dem Datenträger ab, auf dem Ihre Medien gespeichert sind, und halten Sie die gesamte Struktur auf diesem einen Datenträger.

sudo mkdir -p /mnt/data/torrents/movies /mnt/data/torrents/tv
sudo mkdir -p /mnt/data/media/Movies /mnt/data/media/Shows
sudo chown -R 1000:1000 /mnt/data
sudo chmod -R 775 /mnt/data

Prüfen Sie, ob es sich tatsächlich um ein Dateisystem handelt, bevor Sie fortfahren:

df --output=source,target /mnt/data/torrents /mnt/data/media

Beide Zeilen müssen dasselbe Quelldatengerät anzeigen. Bei zwei verschiedenen Geräten funktionieren Hardlinks nie, unabhängig davon, was Sie in der Containerkonfiguration festlegen.

Die Bibliotheksverzeichnisse heißen absichtlich Movies und Shows. Wenn Sie bereits Jellyfin als Medienserver betreiben, binden Sie /mnt/data/media in Jellyfin als /media ein. Dann werden die Bibliotheken unter /media/Movies und /media/Shows abgelegt, genau dort, wo sie in dieser Anleitung vorgesehen sind.

Die Umgebungsdatei

Bewahren Sie die Werte, die sich je nach Server ändern, in .env neben der Compose-Datei auf.

mkdir -p ~/arr && cd ~/arr

Schreiben Sie ~/arr/.env:

PUID=1000
PGID=1000
TZ=Etc/UTC
DATA_ROOT=/mnt/data

Setzen Sie TZ auf Ihre eigene Zeitzone, zum Beispiel Europe/Berlin. Die arr-Anwendungen planen Aufgaben und versehen Protokollzeilen mit Zeitstempeln in dieser Zeitzone. Ein falscher Wert macht später jedes Protokoll unübersichtlich.

Die Compose-Datei

Schreiben Sie ~/arr/docker-compose.yml:

services:
  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./config/prowlarr:/config
    ports:
      - 127.0.0.1:9696:9696
    restart: unless-stopped

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./config/sonarr:/config
      - ${DATA_ROOT}:/data
    ports:
      - 127.0.0.1:8989:8989
    restart: unless-stopped

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - ./config/radarr:/config
      - ${DATA_ROOT}:/data
    ports:
      - 127.0.0.1:7878:7878
    restart: unless-stopped

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - WEBUI_PORT=8080
      - TORRENTING_PORT=6881
    volumes:
      - ./config/qbittorrent:/config
      - ${DATA_ROOT}:/data
    ports:
      - 127.0.0.1:8080:8080
      - 6881:6881
      - 6881:6881/udp
    stop_grace_period: "10s"
    restart: unless-stopped

Vier Dinge in dieser Datei sind entscheidend.

${DATA_ROOT}:/data ist in den drei Containern, die auf Mediendateien zugreifen, identisch. Prowlarr erhält es nicht, weil Prowlarr niemals eine Mediendatei öffnet.

Jeder Webport ist an 127.0.0.1 gebunden. Docker veröffentlicht ihn dadurch nur auf der Loopback-Adresse. Ein einfaches 8989:8989 würde ihn auf jeder Schnittstelle veröffentlichen. Die eigenen Firewall-Regeln von Docker würden diesen Netzwerkverkehr direkt an einer ufw-Regel deny vorbeileiten. Dieses Verhalten überrascht viele Benutzer immer wieder. Es wird unter warum Docker Ports direkt an ufw vorbei veröffentlicht erklärt.

Port 6881 wird absichtlich auf allen Schnittstellen veröffentlicht. Das ist der Listening-Port für Torrents. Er muss für eingehende Peer-Verbindungen erreichbar sein. Erlauben Sie ihn mit sudo ufw allow 6881. Lesen Sie die Grundlagen der ufw-Firewall für einen VPS, wenn dieser Befehl neu für Sie ist.

Die Konfigurationsverzeichnisse sind für jede Anwendung getrennt. Nur das Medienvolume wird gemeinsam verwendet. Erstellen Sie die Verzeichnisse vor dem ersten Start. Dann gehören sie Ihrem Benutzer und nicht root:

mkdir -p ~/arr/config/prowlarr ~/arr/config/sonarr ~/arr/config/radarr ~/arr/config/qbittorrent
docker compose up -d
docker compose ps

Alle vier Dienste sollten running lesen. Im Juli 2026 werden diese Images auf lscr.io veröffentlicht, und der Tag latest folgt dem aktuellen stabilen Release. Verwenden Sie stattdessen ein Versions-Tag, wenn Upgrades eine bewusste Entscheidung und keine Überraschung sein sollen.

Sicher auf die Weboberflächen zugreifen

Da die Ports an Loopback gebunden sind, ist noch nichts erreichbar. Leiten Sie sie von Ihrem eigenen Rechner über SSH weiter:

ssh -L 9696:127.0.0.1:9696 -L 8989:127.0.0.1:8989 \
    -L 7878:127.0.0.1:7878 -L 8080:127.0.0.1:8080 you@your-server

Jetzt erreichen Sie mit http://127.0.0.1:8989 in Ihrem Browser Sonarr auf dem Server. Für einen dauerhaften Zugriff setzen Sie den Stack hinter Traefik mit TLS-Zertifikaten für mehrere Anwendungen, oder greifen Sie über ein WireGuard-VPN, das Sie selbst hosten auf den Server zu. Keine dieser Anwendungen sollte mit nur ihrer eigenen Anmeldeseite im öffentlichen Internet erreichbar sein.

qBittorrent erzeugt beim ersten Start ein zufälliges Administratorkennwort und schreibt es in das Container-Log. Lesen Sie es aus und ändern Sie es anschließend in der Weboberfläche:

docker compose logs qbittorrent | grep -i password

Wenn Sie das Kennwort nicht ändern, wird bei jedem Neustart ein neues zufälliges Kennwort erzeugt. Dann müssen Sie jedes Mal wieder in den Logs nachsehen.

Pfade in jeder Anwendung festlegen

Öffnen Sie in qBittorrent Options und anschließend Downloads. Legen Sie den Standardspeicherpfad auf /data/torrents fest. Belassen Sie den Ordner für unvollständige Downloads innerhalb desselben Verzeichnisbaums, zum Beispiel unter /data/torrents/incomplete. Ein Download, der außerhalb von /data abgeschlossen wird, kann nicht per Hardlink in die Bibliothek übernommen werden.

Öffnen Sie in Sonarr Settings und anschließend Media Management. Fügen Sie den Stammordner /data/media/Shows hinzu. In Radarr lautet der Stammordner /data/media/Movies. Dies sind Pfade innerhalb des Containers. Der Hostpfad /mnt/data/media/Shows wird abgelehnt, weil dieses Verzeichnis aus Sicht des Containers nicht existiert.

Öffnen Sie in Sonarr und Radarr jeweils Settings und anschließend Download Clients. Fügen Sie qBittorrent hinzu. Der Host ist qbittorrent und der Port ist 8080. Der Servicename funktioniert als Hostname, weil Compose alle vier Container in ein Netzwerk mit einem internen DNS-Service (domain name system) einbindet. Verwenden Sie hier nicht localhost: Innerhalb des Sonarr-Containers ist localhost Sonarr.

Lassen Sie Remote Path Mappings leer. Mit dieser Funktion wird ein vom Downloadclient gemeldeter Pfad in einen für die arr-Anwendung sichtbaren Pfad übersetzt. Bei einem gemeinsamen /data-Mount stimmen beide Container bereits bei allen Pfaden überein. Das ist der zweite Grund, warum sich dieser Aufbau lohnt.

Prowlarr mit Sonarr und Radarr verbinden

Prowlarr überträgt Indexerdefinitionen an die anderen Anwendungen. Dadurch konfigurieren Sie einen Indexer nur einmal. Prowlarr benötigt von jeder Anwendung einen API-Schlüssel (Application Programming Interface).

Öffnen Sie in Sonarr Settings und anschließend General. Kopieren Sie den API-Schlüssel. Öffnen Sie in Prowlarr Settings und anschließend Apps. Fügen Sie eine Sonarr-Anwendung hinzu und füllen Sie drei Felder aus. Prowlarr Server ist http://prowlarr:9696. Sonarr Server ist http://sonarr:8989. API Key ist der kopierte Wert. Klicken Sie auf Test. Ein grünes Ergebnis bedeutet, dass Prowlarr Sonarr über das Compose-Netzwerk erreicht hat. Wiederholen Sie den Vorgang mit Radarr unter http://radarr:7878.

Ein rotes Ergebnis mit der Meldung, dass die Verbindung abgelehnt wurde, bedeutet fast immer, dass der Dienstname falsch ist oder das Präfix http:// fehlt. Überprüfen Sie, ob der Name innerhalb des Containers aufgelöst wird:

docker compose exec prowlarr curl -sS -o /dev/null -w '%{http_code}\n' http://sonarr:8989

Ein HTTP-Statuscode bestätigt, dass der Netzwerkpfad funktioniert. Ein Fehler bei der Namensauflösung bestätigt, dass der Dienstname falsch ist.

Verlassen Sie sich erst auf die Einrichtung, wenn Sie die Linkanzahl geprüft haben. Nachdem ein Element importiert wurde, vergleichen Sie die heruntergeladene Datei mit der Datei in der Bibliothek:

stat -c '%i %h %n' /mnt/data/torrents/tv/*/*.mkv
stat -c '%i %h %n' /mnt/data/media/Shows/*/*/*.mkv

Die erste Zahl ist die Inode und die zweite Zahl ist die Linkanzahl. Bei einer per Hardlink verknüpften Datei ist die Inode an beiden Speicherorten identisch und die Linkanzahl beträgt 2. Zwei verschiedene Inodes mit jeweils einer Linkanzahl von 1 bedeuten, dass Sonarr die Datei kopiert hat. Im Importprotokoll steht dann, dass der Hardlink fehlgeschlagen ist.

Beobachten Sie auch den Speicherplatz. df -h /mnt/data sollte sich bei einem Import kaum verändern, da ein Hardlink nur einen Namen und keine Daten hinzufügt.

Was tatsächlich fehlschlägt

Berechtigungsfehler beim Import bedeuten, dass die Benutzer-ID des Containers nicht in den Bibliotheksordner schreiben kann. Die Meldung lautet Access to the path ... is denied. Prüfen Sie mit ls -ln /mnt/data/media, ob die Eigentümer-ID Ihrer PUID entspricht. Beachten Sie außerdem, dass Verzeichnisse das Ausführungsbit benötigen, bevor der Container sie betreten kann.

Dateien, die scheinbar root gehören, weisen darauf hin, dass der Container gestartet wurde, bevor das Verzeichnis auf dem Host existierte. Docker hat es daher als root erstellt. Beenden Sie den Stack, chown Sie das Verzeichnis und starten Sie den Stack erneut.

Wenn Sie einen Torrent in qBittorrent löschen und die Bibliotheksdatei anschließend verschwunden ist, war der Import entweder eine Kopie, die später entfernt wurde, oder Sie haben die Daten statt des Torrent-Eintrags gelöscht. Bei einem echten Hardlink bleibt der andere Name erhalten, wenn ein Name entfernt wird. Die Daten werden erst freigegeben, wenn die Linkanzahl null erreicht.

Wenn sich eine Festplatte schneller füllt als durch die hinzugefügten Medien zu erwarten ist, liegt das Problem wahrscheinlich an Kopien. Führen Sie die obige stat-Prüfung durch, bevor Sie zusätzlichen Speicher kaufen.

Anforderungen dieses Stacks an einen VPS

Die drei arr-Anwendungen benötigen wenige Ressourcen. Sie fragen Indexer regelmäßig ab, schreiben in eine kleine SQLite-Datenbank und benennen Dateien um. Ein Server mit 2 GB RAM führt alle vier Container problemlos aus. Die Last entsteht an anderer Stelle. Ein Download-Client kann bei großen Torrents die Datenträger-Ein- und -Ausgabe auslasten. Ein auf demselben System laufender Media-Server beansprucht beim Transcodieren von Videos die CPU. Speichern Sie Mediendaten auf einem Volume mit ausreichendem Datendurchsatz. Legen Sie eine Bandbreitenbegrenzung für den Download-Client fest, wenn der Server noch andere wichtige Aufgaben ausführt.

FAQ

Der Grund ist, dass sich Quelle und Ziel aus Sicht des Containers auf unterschiedlichen Dateisystemen befinden. Zwei separate Bind-Mounts wie /downloads und /tv gelten als zwei Dateisysteme, auch wenn beide vom selben Host-Datenträger stammen. Binden Sie in jedem Container ein gemeinsames übergeordnetes Verzeichnis als /data ein. Legen Sie Downloads und die Bibliothek darin ab. Dann ist der Link möglich. Bestätigen Sie das Ergebnis mit stat -c '%i %h %n' für beide Dateien. Sie müssen dieselbe Inode und eine Link-Anzahl von 2 aufweisen.

Welche PUID und PGID sollte ich verwenden?

Verwenden Sie die numerische ID des Hostkontos, dem der Medienbaum gehört. Diese erhalten Sie mit id -u und id -g. Auf einem frisch eingerichteten Ubuntu-VPS ist das normalerweise für beide Werte 1000. Jeder Container im Stack muss dasselbe Paar verwenden. Andernfalls schreibt eine Anwendung Dateien, die eine andere Anwendung nicht ändern kann. Erstellen Sie die Container nach der Änderung der Werte mit docker compose up -d --force-recreate neu. Korrigieren Sie anschließend die vorhandenen Dateien mit chown -R.

Muss ich diese Weboberflächen im Internet verfügbar machen?

Nein, das sollten Sie nicht tun. Binden Sie jeden veröffentlichten Port in der Compose-Datei an 127.0.0.1. Greifen Sie anschließend über einen SSH-Tunnel, ein VPN oder einen Reverse-Proxy auf die Oberflächen zu. Dieser beendet TLS (Transport Layer Security) und fügt eine eigene Authentifizierung hinzu. Die direkte Veröffentlichung ist problematischer als erwartet. Docker fügt eigene Firewallregeln ein, und eine ufw-Regel deny blockiert diesen Datenverkehr nicht.

Wo finde ich das qBittorrent-Passwort?

Das LinuxServer.io-Image schreibt ein temporäres Passwort für den Benutzer admin in das Startprotokoll. Führen Sie docker compose logs qbittorrent | grep -i password aus, um es zu lesen. Legen Sie anschließend unter Options und Web UI ein dauerhaftes Passwort fest. Bei jedem Neustart wird ein neues temporäres Passwort erzeugt, bis Sie ein eigenes festlegen.

Kann Jellyfin dieselben Verzeichnisse verwenden?

Ja. Genau dafür ist dieses Layout vorgesehen. Binden Sie /mnt/data/media in Ihren Mediaserver als /media ein. Seine Bibliotheken befinden sich dann unter /media/Movies und /media/Shows, während Sonarr und Radarr über /data/media in dieselben Verzeichnisse schreiben. Geben Sie dem Mediaserver dieselben PUID und PGID, damit er die vom arr-Stack geschriebenen Dateien lesen kann.

#sonarr#radarr#prowlarr#docker-compose#self-hosting