Stos arr w jednym pliku Docker Compose
Uruchom Prowlarr, Sonarr, Radarr i qBittorrent na VPS z jednym plikiem Docker Compose. Poznaj wspólne PUID, PGID i układ woluminów dla hardlinków.
Co budujesz
Stos arr Docker Compose składa się z czterech kontenerów zarządzających biblioteką multimediów: Prowlarr obsługuje konfigurację indeksatorów, Sonarr zarządza serialami, Radarr filmami, a qBittorrent jest klientem pobierania. Kontenery komunikują się za pośrednictwem sieci Compose, używając nazw usług, i współdzielą jedno drzewo katalogów na hoście. Instalacja jest krótka. O tym, czy stos będzie działać przez lata, czy też co tydzień będzie sprawiać problemy, decyduje przede wszystkim układ woluminów. Dlatego większość tego poradnika dotyczy właśnie tego zagadnienia.
Stos nie wyszukuje treści samodzielnie. Prowlarr przechowuje dodane do niego indeksatory, a wybór używanych indeksatorów pozostaje po Twojej stronie i wiąże się z odpowiedzialnością prawną. Ten poradnik obejmuje konfigurację infrastruktury: użytkowników, ścieżki, uprawnienia, komunikację sieciową kontenerów oraz testy potwierdzające poprawne działanie.
Jeśli nigdy nie tworzono pliku Compose, należy najpierw przeczytać podstawy Docker Compose dla VPS. W tym artykule przyjęto, że docker compose version wyświetla już wynik na serwerze.
Dlaczego twarde dowiązania nie działają i dlaczego to jest najważniejsze
Po zakończeniu pobierania Sonarr importuje plik do biblioteki. Jeśli folder pobierania i folder biblioteki znajdują się w tym samym systemie plików, import jest twardym dowiązaniem: drugą nazwą wskazującą na te same dane na dysku. Nie zajmuje dodatkowego miejsca ani czasu. Torrent nadal udostępnia dane pod starą nazwą, a serwer multimediów odczytuje je pod nową.
Jeśli oba foldery znajdują się w różnych systemach plików, jądro nie może utworzyć takiego dowiązania. Sonarr wykonuje wtedy kopię. Sezon o rozmiarze 40 GB zajmuje teraz 80 GB miejsca na dysku, a operacje wejścia i wyjścia trwają kilka minut. Dziennik importu rejestruje, że utworzenie twardego dowiązania nie powiodło się i plik został skopiowany. Na VPS z ustalonym limitem miejsca na dysku właśnie w ten sposób można wyczerpać dostępne miejsce w ciągu tygodnia.
Problem polega na tym, że w kontenerze bind mount stanowi granicę systemu plików. Zamontowanie /mnt/data/torrents jako /downloads oraz /mnt/data/media jako /tv powoduje, że mimo przechowywania obu ścieżek na jednym dysku hosta Sonarr widzi dwa osobne punkty montowania i odmawia utworzenia dowiązania między nimi. Dokumentacja oficjalnego obrazu LinuxServer.io wskazuje to bezpośrednio: użycie osobnych ścieżek /downloads i /tv uniemożliwia tworzenie twardych dowiązań.
Rozwiązaniem jest jedno montowanie. Każdy kontener korzystający z multimediów otrzymuje ten sam pojedynczy wolumin, /mnt/data:/data, a każda używana przez niego ścieżka jest folderem w jego obrębie. Jeden punkt montowania, jeden system plików i działające twarde dowiązania.
Tworzenie użytkownika, grupy i katalogów
Kontenery zapisują pliki jako numeryczny identyfikator użytkownika, ustawiany przez PUID i PGID. Należy użyć własnego konta, aby można było odczytywać i edytować te pliki przez SSH bez użycia sudo.
id -u
id -gObie komendy zwykle wyświetlają 1000 na świeżo utworzonym VPS z Ubuntu. Następnie należy utworzyć strukturę katalogów. Należy umieścić ją na dysku zawierającym multimedia i przechowywać całą strukturę na jednym dysku.
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/dataPrzed kontynuowaniem należy sprawdzić, czy rzeczywiście jest to jeden system plików:
df --output=source,target /mnt/data/torrents /mnt/data/mediaOba wiersze muszą wyświetlać to samo urządzenie źródłowe. Dwa różne urządzenia oznaczają, że hardlinki nigdy nie będą działać, niezależnie od ustawień konfiguracji kontenera.
Katalogi biblioteki celowo noszą nazwy Movies i Shows. Jeśli Jellyfin jest już używany jako serwer multimediów, należy zamontować /mnt/data/media w Jellyfin jako /media. Biblioteki będą wtedy dostępne w katalogach /media/Movies i /media/Shows, dokładnie tam, gdzie umieszcza je ten poradnik.
Plik środowiskowy
Wartości zmienne dla poszczególnych serwerów należy przechowywać w .env, obok pliku Compose.
mkdir -p ~/arr && cd ~/arrNależy zapisać ~/arr/.env:
PUID=1000
PGID=1000
TZ=Etc/UTC
DATA_ROOT=/mnt/dataNależy ustawić TZ na własną strefę, na przykład Europe/Berlin. Aplikacje arr planują zadania i zapisują znaczniki czasu wierszy dziennika w tej strefie, dlatego nieprawidłowa wartość spowoduje później nieczytelność wszystkich dzienników.
Plik Compose
Należy zapisać ~/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-stoppedCztery elementy tego pliku wykonują istotne zadania.
${DATA_ROOT}:/data jest identyczny w trzech kontenerach, które mają dostęp do plików multimedialnych. Prowlarr go nie używa, ponieważ Prowlarr nigdy nie otwiera pliku multimedialnego.
Każdy port interfejsu sieciowego jest przypisany do 127.0.0.1, dlatego Docker publikuje go wyłącznie na adresie loopback. Zwykłe 8989:8989 opublikowałoby port na każdym interfejsie, a własne reguły zapory Dockera przekazałyby ten ruch bezpośrednio poza regułę deny ufw. To zachowanie często zaskakuje użytkowników. Wyjaśniono je w dlaczego Docker publikuje porty bezpośrednio przez ufw.
Port 6881 jest celowo publikowany na wszystkich interfejsach. Jest to port nasłuchiwania połączeń torrent i musi być dostępny dla przychodzących połączeń peerów. Należy zezwolić na ten port za pomocą sudo ufw allow 6881. Jeśli to polecenie jest nowe, należy zapoznać się także z podstawami zapory ufw dla VPS.
Katalogi konfiguracyjne są oddzielne dla każdej aplikacji, a współdzielony jest tylko wolumin z plikami multimedialnymi. Należy utworzyć je przed pierwszym uruchomieniem, aby należały do bieżącego użytkownika, a nie do root:
mkdir -p ~/arr/config/prowlarr ~/arr/config/sonarr ~/arr/config/radarr ~/arr/config/qbittorrent
docker compose up -d
docker compose psWszystkie cztery usługi powinny używać running. W lipcu 2026 te obrazy są publikowane na lscr.io, a znacznik latest wskazuje bieżące wydanie stabilne. Jeśli aktualizacje mają wymagać świadomej decyzji, a nie powodować nieoczekiwanych zmian, należy zamiast niego przypiąć znacznik wersji.
Bezpieczny dostęp do interfejsów internetowych
Ponieważ porty są powiązane z interfejsem loopback, nie są jeszcze dostępne z zewnątrz. Należy przekierować je przez SSH z własnego komputera:
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-serverTeraz http://127.0.0.1:8989 w przeglądarce otwiera Sonarr na serwerze. Aby zapewnić stały dostęp, należy umieścić stos za Traefik z certyfikatami TLS dla kilku aplikacji albo uzyskać dostęp do serwera przez samodzielnie hostowaną sieć VPN WireGuard. Żadna z tych aplikacji nie powinna być dostępna bezpośrednio z publicznego internetu i chroniona wyłącznie własną stroną logowania.
qBittorrent podczas pierwszego uruchomienia generuje losowe hasło administratora i zapisuje je w logu kontenera. Należy je odczytać, a następnie zmienić w interfejsie internetowym:
docker compose logs qbittorrent | grep -i passwordJeśli hasło nie zostanie zmienione, przy każdym ponownym uruchomieniu zostanie wygenerowane nowe losowe hasło i za każdym razem konieczne będzie ponowne sprawdzanie logów.
Ustaw ścieżki w każdej aplikacji
W qBittorrent otwórz Options, a następnie Downloads i ustaw domyślną ścieżkę zapisu na /data/torrents. Folder niekompletnych pobrań pozostaw w tej samej hierarchii, na przykład /data/torrents/incomplete. Pobrania zakończone w dowolnym miejscu poza /data nie mogą zostać połączone twardym dowiązaniem z biblioteką.
W Sonarr otwórz Settings, a następnie Media Management i dodaj folder główny /data/media/Shows. W Radarr folderem głównym jest /data/media/Movies. Są to ścieżki wewnątrz kontenera. Ścieżka hosta /mnt/data/media/Shows zostanie odrzucona, ponieważ z punktu widzenia kontenera ten katalog nie istnieje.
W Sonarr i Radarr otwórz Settings, a następnie Download Clients i dodaj qBittorrent. Hostem jest qbittorrent, a portem 8080. Nazwa usługi działa jako nazwa hosta, ponieważ Compose umieszcza wszystkie cztery kontenery w jednej sieci z wewnętrzną usługą DNS (domain name system). Nie używaj tutaj localhost: wewnątrz kontenera Sonarr wartość localhost oznacza Sonarr.
Pozostaw sekcję Remote Path Mappings pustą. Ta funkcja służy do tłumaczenia ścieżki zgłaszanej przez klienta pobierania na ścieżkę widoczną dla aplikacji arr. Przy jednym współdzielonym montowaniu /data oba kontenery już używają tych samych ścieżek. Jest to drugi powód, dla którego warto zastosować ten układ.
Podłączanie Prowlarr do Sonarr i Radarr
Prowlarr przekazuje definicje indeksatorów do innych aplikacji, dlatego indeksator konfiguruje się tylko raz. Do tego celu wymagany jest klucz API (interfejsu programistycznego aplikacji) z każdej aplikacji.
W Sonarr otwórz Settings, a następnie General, i skopiuj klucz API. W Prowlarr otwórz Settings, a następnie Apps, dodaj aplikację Sonarr i uzupełnij trzy pola. Prowlarr Server to http://prowlarr:9696. Sonarr Server to http://sonarr:8989. API Key to skopiowana wartość. Naciśnij Test. Zielony wynik oznacza, że Prowlarr połączył się z Sonarr przez sieć Compose. Powtórz te czynności dla Radarr pod adresem http://radarr:7878.
Czerwony wynik z komunikatem o odmowie połączenia prawie zawsze oznacza nieprawidłową nazwę usługi albo brak prefiksu http://. Sprawdź, czy nazwa jest rozpoznawana z wnętrza kontenera:
docker compose exec prowlarr curl -sS -o /dev/null -w '%{http_code}\n' http://sonarr:8989Kod stanu HTTP potwierdza, że ścieżka sieciowa działa prawidłowo. Błąd rozpoznawania nazwy potwierdza, że nazwa usługi jest nieprawidłowa.
Sprawdzenie, czy rzeczywiście utworzono dowiązania twarde
Nie należy uznawać konfiguracji za poprawną, dopóki nie zostanie sprawdzona liczba dowiązań. Po zaimportowaniu jednego elementu należy porównać pobrany plik z plikiem w bibliotece:
stat -c '%i %h %n' /mnt/data/torrents/tv/*/*.mkv
stat -c '%i %h %n' /mnt/data/media/Shows/*/*/*.mkvPierwsza liczba oznacza numer inode, a druga — liczbę dowiązań. W przypadku dowiązania twardego w obu lokalizacjach widoczny jest ten sam inode, a liczba dowiązań wynosi 2. Dwa różne inode, z których każdy ma liczbę dowiązań równą 1, oznaczają, że Sonarr skopiował plik. W dzienniku importu będzie wtedy informacja o nieudanym utworzeniu dowiązania twardego.
Należy również obserwować zajętość dysku. df -h /mnt/data powinno zmienić się tylko nieznacznie podczas importu, ponieważ dowiązanie twarde dodaje nazwę, ale nie kopiuje danych.
Co faktycznie się psuje
Błędy uprawnień podczas importowania oznaczają, że identyfikator użytkownika kontenera nie może zapisywać w folderze biblioteki. Komunikat ma postać Access to the path ... is denied. Za pomocą ls -ln /mnt/data/media należy sprawdzić, czy identyfikator właściciela odpowiada wartości PUID. Należy pamiętać, że katalogi wymagają bitu wykonywania, zanim kontener będzie mógł do nich wejść.
Pliki, których właścicielem jest root, oznaczają, że kontener został uruchomiony przed utworzeniem katalogu na hoście. Docker utworzył go wtedy jako root. Należy zatrzymać stos, chown katalog, a następnie uruchomić stos ponownie.
Usunięcie torrenta z qBittorrent i stwierdzenie, że pliku biblioteki już nie ma, oznacza, że import był kopiowaniem, po którym kopia została usunięta, albo że usunięto dane zamiast wpisu torrenta. W przypadku rzeczywistego hardlinku usunięcie jednej nazwy pozostawia drugą bez zmian, ponieważ dane są zwalniane dopiero wtedy, gdy liczba dowiązań spadnie do zera.
Dysk zapełniający się szybciej niż ilość dodanych multimediów wskazuje na najbardziej kosztowną formę problemu z kopiowaniem. Przed zakupem dodatkowej pamięci masowej należy wykonać powyższe sprawdzenie stat.
Wymagania tego stosu wobec VPS
Trzy aplikacje arr mają niewielkie wymagania. Odpytują indeksatory, zapisują dane w małej bazie SQLite i zmieniają nazwy plików. Serwer z 2 GB pamięci RAM bez problemu uruchomi wszystkie cztery kontenery. Obciążenie pochodzi z innych zadań. Klient pobierania może całkowicie obciążyć wejście i wyjście dysku podczas obsługi dużych torrentów, a serwer multimediów transkodujący wideo na tym samym komputerze obciąży procesor. Dane multimedialne należy przechowywać na woluminie zapewniającym rzeczywistą przepustowość. Jeśli na serwerze wykonywane są inne istotne zadania, należy ustawić limit przepustowości w kliencie pobierania.
FAQ
Dlaczego Sonarr kopiuje pliki zamiast tworzyć do nich hardlinki?
Źródło i miejsce docelowe znajdują się z punktu widzenia kontenera w różnych systemach plików. Dwa oddzielne montowania bind, takie jak /downloads i /tv, są traktowane jako różne systemy plików, nawet jeśli oba pochodzą z jednego dysku hosta. Należy zamontować jeden katalog nadrzędny jako /data w każdym kontenerze oraz umieścić w nim katalog pobierania i bibliotekę. Wtedy utworzenie linku będzie możliwe. Wynik należy potwierdzić za pomocą stat -c '%i %h %n' dla obu plików: ten sam inode i liczba dowiązań równa 2.
Jakich wartości PUID i PGID należy użyć?
Należy użyć numerycznego identyfikatora konta hosta, które jest właścicielem drzewa multimediów. Identyfikatory można uzyskać za pomocą id -u i id -g. W nowo utworzonej maszynie VPS z Ubuntu jest to zwykle 1000 dla obu wartości. Każdy kontener w stosie musi używać tej samej pary. W przeciwnym razie jedna aplikacja będzie zapisywać pliki, których inna nie będzie mogła modyfikować. Po zmianie wartości należy odtworzyć kontenery za pomocą docker compose up -d --force-recreate oraz poprawić istniejące pliki za pomocą chown -R.
Czy należy udostępniać te interfejsy webowe w internecie?
Nie. Nie należy tego robić. Każdy publikowany port należy powiązać z 127.0.0.1 w pliku Compose, a następnie uzyskiwać dostęp do interfejsów przez tunel SSH, VPN albo reverse proxy, które kończy połączenie TLS (transport layer security) i zapewnia własne uwierzytelnianie. Bezpośrednie publikowanie tych interfejsów jest bardziej ryzykowne, niż mogłoby się wydawać, ponieważ Docker dodaje własne reguły zapory sieciowej, a reguła ufw deny nie zablokuje tego ruchu.
Gdzie znaleźć hasło qBittorrent?
Obraz LinuxServer.io zapisuje tymczasowe hasło użytkownika admin w logu uruchomieniowym. Należy uruchomić docker compose logs qbittorrent | grep -i password, aby je odczytać, a następnie ustawić stałe hasło w sekcji Options i Web UI. Przy każdym ponownym uruchomieniu generowane jest nowe hasło tymczasowe, dopóki nie zostanie ustawione własne hasło.
Czy Jellyfin może używać tych samych katalogów?
Tak. Taki jest cel tego układu katalogów. Należy zamontować /mnt/data/media w serwerze multimediów jako /media. Jego biblioteki będą znajdować się w /media/Movies i /media/Shows, a Sonarr i Radarr będą zapisywać dane w tych samych katalogach za pośrednictwem /data/media. Serwerowi multimediów należy nadać te same wartości PUID i PGID, aby mógł odczytywać pliki zapisywane przez stos arr.