SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-28

Docker Compose arr stack: Prowlarr, Sonarr at Radarr

Patakbuhin ang Prowlarr, Sonarr, Radarr at qBittorrent sa isang Docker Compose file, gamit ang iisang PUID, PGID at volume layout para gumana ang hardlinks.

Mga bubuuin mo

Ang Docker Compose arr stack ay binubuo ng apat na container na namamahala sa media library: Prowlarr para sa mga setting ng indexer, Sonarr para sa mga series, Radarr para sa mga pelikula, at qBittorrent bilang download client. Nag-uusap ang mga ito sa Compose network gamit ang service name, at iisang folder tree sa host ang ginagamit nila. Maikli ang installation. Ang volume layout ang pangunahing nagtatakda kung gagana nang maayos ang stack sa loob ng maraming taon o magdudulot ito ng problema bawat linggo, kaya dito nakatuon ang malaking bahagi ng gabay na ito.

Hindi awtomatikong naghahanap ng content ang stack para sa iyo. Nasa Prowlarr ang anumang indexer na idaragdag mo rito, at ikaw ang nagpapasya kung aling mga indexer ang gagamitin mo at ikaw rin ang may legal na pananagutan para rito. Saklaw ng gabay na ito ang plumbing: mga user, path, permission, container networking, at mga check na nagpapatunay na gumagana ito.

Kung hindi ka pa nakakasulat ng Compose file, basahin muna ang mga pangunahing kaalaman sa Docker Compose para sa isang VPS. Ipinapalagay ng post na ito na ang docker compose version ay may inilalabas nang output sa server mo.

Kapag tapos na ang Sonarr sa isang download, ini-import nito ang file sa iyong library. Kung nasa iisang filesystem ang download folder at library folder, hardlink ang import: pangalawang pangalan ito na tumutukoy sa parehong data sa disk. Wala itong karagdagang kinakailangang space o oras. Patuloy na nagse-seed ang torrent gamit ang dating pangalan habang binabasa ng media server ang bago.

Kung magkaiba ang filesystem ng dalawang folder, hindi magagawa ng kernel ang link na iyon. Magfa-fallback ang Sonarr sa pagkopya. Ang isang 40 GB na season ay mangangailangan na ngayon ng 80 GB na disk space at ilang minuto ng input at output. Itinatala rin sa import log na nabigo ang hardlink at sa halip ay kinopya ang file. Sa isang VPS na may nakatakdang disk allowance, ganito nauubusan ng space ang mga user sa loob ng isang linggo.

Narito ang madalas na problema. Sa loob ng container, filesystem boundary ang bind mount. I-mount ang /mnt/data/torrents bilang /downloads at ang /mnt/data/media bilang /tv. Kahit nasa iisang host disk ang mga ito, nakikita ng Sonarr ang dalawang magkahiwalay na mount at tumatangging gumawa ng link sa pagitan ng mga ito. Direktang sinasabi ito sa official LinuxServer.io image documentation: kapag magkahiwalay na /downloads at /tv ang ginamit na paths, mawawala ang kakayahang gumamit ng hardlink.

Ang solusyon ay isang mount lamang. Ang bawat container na gumagamit ng media ay dapat makatanggap ng parehong iisang volume, /mnt/data:/data, at ang bawat path na ginagamit nito ay dapat folder sa loob ng volume na iyon. Isang mount point, isang filesystem, at gumaganang hardlinks.

Gawin ang user, group, at mga folder

Nagsusulat ang mga container ng mga file gamit ang numeric user ID na itinatakda ng PUID at PGID. Gamitin ang sarili mong account para mabasa at ma-edit mo ang mga file na iyon sa SSH nang hindi gumagamit ng sudo.

id -u
id -g

Karaniwan, parehong nagpi-print ang mga ito ng 1000 sa bagong Ubuntu VPS. Ngayon, gawin ang directory tree. Ilagay ito sa disk na naglalaman ng iyong media, at panatilihin ang buong tree sa disk na iyon.

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

Tiyaking iisang filesystem talaga ito bago magpatuloy:

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

Dapat magpakita ang dalawang linya ng parehong source device. Kung magkaiba ang device, hindi kailanman gagana ang hardlinks, anuman ang itakda mo sa container config.

Sadyang pinangalanang Movies at Shows ang mga library folder. Kung gumagamit ka na ng Jellyfin bilang media server, i-mount ang /mnt/data/media sa Jellyfin bilang /media, at mapupunta ang mga library nito sa /media/Movies at /media/Shows, eksaktong gaya ng inilagay ng gabay na iyon.

Ang environment file

Panatilihin sa .env ang mga value na nagbabago depende sa server, katabi ng Compose file.

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

Isulat ang ~/arr/.env:

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

Itakda ang TZ sa sarili mong zone, gaya ng Europe/Berlin. Ginagamit ng arr applications ang zone na iyon sa pag-schedule ng mga task at paglalagay ng timestamp sa mga log line, kaya magiging nakalilito ang lahat ng log kapag mali ang value.

Ang Compose file

Isulat ang ~/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

May apat na bagay sa file na ito na aktuwal na gumaganap ng mahalagang tungkulin.

Magkapareho ang ${DATA_ROOT}:/data sa tatlong container na gumagamit ng media. Hindi ito kailangan ng Prowlarr dahil hindi kailanman nagbubukas ang Prowlarr ng media file.

Naka-bind ang bawat web port sa 127.0.0.1, kaya inilalathala ito ng Docker sa loopback address lamang. Ang plain na 8989:8989 ay maglalathala nito sa bawat interface, at direktang ipapadaan ng sariling firewall rules ng Docker ang traffic na iyon sa deny rule ng ufw. Palaging nagugulat dito ang mga tao, at ipinaliliwanag ito sa kung bakit direktang inilalathala ng Docker ang mga port sa ufw.

Sadyang inilalathala ang port 6881 sa lahat ng interface. Ito ang torrent listening port, at dapat itong maabot para sa mga incoming peer connection. Payagan ito gamit ang sudo ufw allow 6881, at basahin ang mga pangunahing kaalaman sa ufw firewall para sa VPS kung bago sa iyo ang command na iyon.

Magkahiwalay ang mga config directory ng bawat application, at ang media volume lamang ang pinagsasaluhan. Gawin ang mga ito bago ang unang pagsisimula upang pagmamay-ari sila ng iyong user sa halip na ng root:

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

Dapat gamitin ng lahat ng apat na serbisyo ang running. Noong July 2026, inilalathala ang mga image na ito sa lscr.io, at sinusundan ng latest tag ang kasalukuyang stable release. Gumamit na lamang ng version tag kung gusto mong ang mga upgrade ay resulta ng desisyon at hindi ng sorpresa.

Ligtas na pag-access sa mga web interface

Dahil nasa loopback ang mga port, wala pang nakalantad sa internet. I-forward ang mga ito sa SSH mula sa sarili mong machine:

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

Ngayon, maa-access ng http://127.0.0.1:8989 sa browser mo ang Sonarr sa server. Para sa permanenteng access, ilagay ang stack sa likod ng Traefik na may TLS certificates para sa ilang app, o i-access ang server gamit ang WireGuard VPN na ikaw mismo ang nag-host. Hindi dapat direktang nasa public internet ang alinman sa mga application na ito na sarili lang nitong login page ang proteksiyon. Kung pipiliin mo ang reverse proxy route at mas gusto mong isang account lang para sa apat na interface kaysa apat na magkakahiwalay na application login na kailangan mong pamahalaan, nagbibigay ang Authentik ng self-hosted single sign-on na maaaring ipatupad ng Traefik sa bawat request gamit ang forward auth.

Gumagawa ang qBittorrent ng random administrator password sa unang pagsisimula at ipinapakita ito sa container log. Basahin ito, pagkatapos ay palitan sa web interface:

docker compose logs qbittorrent | grep -i password

Kung hindi mo ito papalitan, bagong random password ang gagawin sa bawat restart, at kailangan mong bumalik sa logs sa bawat pagkakataon.

Itakda ang mga path sa loob ng bawat application

Sa qBittorrent, buksan ang Options, pagkatapos ay Downloads, at itakda ang default save path sa /data/torrents. Panatilihin ang incomplete-downloads folder sa loob ng parehong tree, gaya ng /data/torrents/incomplete. Ang download na natapos sa labas ng /data ay hindi maaaring lagyan ng hard link papunta sa library.

Sa Sonarr, buksan ang Settings, pagkatapos ay Media Management, at idagdag ang root folder na /data/media/Shows. Sa Radarr, ang root folder ay /data/media/Movies. Mga path na ito sa loob ng container. Tinatanggihan ang host path na /mnt/data/media/Shows dahil hindi umiiral ang directory na iyon mula sa pananaw ng container.

Sa Sonarr at Radarr, buksan ang Settings, pagkatapos ay Download Clients, at idagdag ang qBittorrent. Ang host ay qbittorrent at ang port ay 8080. Gumagana ang service name bilang hostname dahil inilalagay ng Compose ang apat na container sa iisang network na may internal DNS (domain name system) service. Huwag gamitin ang localhost dito: sa loob ng Sonarr container, ang localhost ay Sonarr.

Iwanang walang laman ang Remote Path Mappings. Ginagamit ang feature na ito para isalin ang path na iniuulat ng download client tungo sa path na nakikita ng arr application. Dahil iisang shared /data mount ang ginagamit, pareho nang nakikita ng dalawang container ang bawat path. Ito ang ikalawang dahilan kung bakit sulit ang ganitong layout.

Ikonekta ang Prowlarr sa Sonarr at Radarr

Ipinapasa ng Prowlarr ang mga definition ng indexer sa iba pang application, kaya isang beses mo lang kino-configure ang indexer. Kailangan nito ng API (application programming interface) key mula sa bawat application.

Sa Sonarr, buksan ang Settings, pagkatapos ay General, at kopyahin ang API key. Sa Prowlarr, buksan ang Settings, pagkatapos ay Apps, magdagdag ng Sonarr application, at punan ang tatlong field. Ang Prowlarr Server ay http://prowlarr:9696. Ang Sonarr Server ay http://sonarr:8989. Ang API Key ay ang kinopyang value. Pindutin ang Test. Ibig sabihin ng green na resulta ay naabot ng Prowlarr ang Sonarr sa pamamagitan ng Compose network. Ulitin ito para sa Radarr sa http://radarr:7878.

Ang red na resultang nagsasabing tinanggihan ang connection ay halos palaging nangangahulugan ng maling service name o nawawalang http:// prefix. Kumpirmahing nagre-resolve ang pangalan mula sa loob ng container:

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

Pinatutunayan ng HTTP status code na maayos ang network path. Pinatutunayan ng name resolution error na mali ang service name.

Huwag pagkatiwalaan ang setup hangga't hindi mo nakikita ang link count. Pagkatapos ma-import ang isang item, ikumpara ang na-download na file sa file sa library:

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

Ang unang numero ay ang inode at ang ikalawa ay ang link count. Ang file na ginamitan ng hardlink ay may parehong inode sa dalawang lokasyon at may link count na 2. Kapag magkaiba ang inode at parehong may link count na 1, kinopya ni Sonarr ang file. Isasaad sa import log na nabigo ang hardlink.

Bantayan din ang disk. Halos hindi dapat gumalaw ang df -h /mnt/data kapag may import, dahil nagdaragdag lamang ng pangalan ang hardlink at walang data.

Ano talaga ang nasisira

Ang mga permission error habang nag-i-import ay nangangahulugang hindi makapagsulat ang user id ng container sa library folder. Ang mensahe ay Access to the path ... is denied. Gamitin ang ls -ln /mnt/data/media upang tiyaking tugma ang owner id sa iyong PUID. Tandaan na kailangan ng mga directory ang execute bit bago mapasok ng container ang mga ito.

Ang mga file na lumalabas na pagmamay-ari ng root ay nangangahulugang nagsimula ang container bago umiral ang directory sa host, kaya ginawa ito ng Docker bilang root. Ihinto ang stack, chown ang directory, at simulan itong muli.

Kapag nag-delete ka ng torrent sa qBittorrent at nawala ang file sa library, nangangahulugan ito na copy ang import na kalaunan ay natanggal, o dinelete mo ang data sa halip na ang torrent entry. Sa tunay na hardlink, mananatiling buo ang kabilang pangalan kapag inalis ang isa, dahil nare-release lamang ang data kapag umabot sa zero ang link count.

Ang disk na mas mabilis mapuno kaysa sa dami ng media na idinagdag mo ay ang pinakamagastos na anyo ng copy problem. Patakbuhin muna ang stat check sa itaas bago bumili ng karagdagang storage.

Mga kailangan ng stack na ito mula sa VPS

Magaan ang tatlong arr application. Nagpo-poll ang mga ito ng indexer, nagsusulat sa maliit na SQLite database, at nagre-rename ng mga file. Kayang patakbuhin ng server na may 2 GB na RAM ang lahat ng apat na container nang maayos. Sa ibang bahagi nanggagaling ang load. Sumasagad sa disk input at output ang download client kapag malalaki ang torrent, at gagamitin ng media server na nagta-transcode ng video sa parehong box ang CPU. Ilagay ang media sa volume na may aktuwal na throughput, at magtakda ng bandwidth limit sa download client kung may iba ka pang mahalagang pinapagawa sa server. Maglaan ng hiwalay na budget para sa ibang workload sa halip na ipagpalagay na sapat ang headroom: ang self-hosted na AFFiNE workspace ay isa pang apat na container na may database sa likod ng mga ito, at sa isang 2 GB box, karamihan ng memory ay kakailanganin nito. Hindi ganoon kalaki ang kinakailangan ng bawat karagdagang service: ang single-purpose na gaya ng self-hosted na openGym workout tracker ay maayos na nakikihati sa box, basta bigyan mo ito ng sarili nitong TLS at alamin kung saan nakalagay ang database file nito bago mo ito pagkatiwalaan ng isang taong training history. Ang anumang may web application, Postgres database, at background worker queue ay mas malapit sa AFFiNE end ng saklaw na iyon, kaya magpasya muna kung ang self-hosted na Chatwoot support desk ay dapat ilagay sa server na ito o sa sarili nitong server bago mo matuklasan ang limit sa kalagitnaan ng isang import. Mas kailangan ng pag-iingat sa mga bursty workload, dahil ang peak, hindi ang average, ang sumasalungat sa isang import: kung isinasaalang-alang mo ang self-hosted na OneCLI na nagbibigay sa bawat tao ng sarili nitong sandboxed agent, ihambing ang published sizing numbers nito sa aktuwal na libreng resource habang tuloy-tuloy sa pinakamataas na bilis ang qBittorrent, hindi sa ipinapakita ng free -h kapag idle ang box.

FAQ

Dahil magkaibang filesystem ang source at destination mula sa pananaw ng container. Ang dalawang magkahiwalay na bind mount, gaya ng /downloads at /tv, ay itinuturing na dalawang filesystem kahit mula sa iisang host disk ang mga ito. I-mount ang iisang parent directory bilang /data sa bawat container, at ilagay dito ang downloads at library para maging posible ang link. Kumpirmahin ang resulta gamit ang stat -c '%i %h %n' sa parehong files: dapat pareho ang inode at may link count na 2.

Anong PUID at PGID ang dapat kong gamitin?

Gamitin ang numeric ID ng host account na nagmamay-ari ng media tree. Makukuha mo ito gamit ang id -u at id -g. Sa bagong Ubuntu VPS, karaniwang 1000 ang pareho. Dapat gamitin ng bawat container sa stack ang parehong pair. Kung hindi, maaaring magsulat ang isang application ng files na hindi mabago ng ibang application. Pagkatapos baguhin ang mga value, gawin muli ang mga container gamit ang docker compose up -d --force-recreate at ayusin ang mga kasalukuyang files gamit ang chown -R.

Kailangan ko bang ilantad sa internet ang mga web interface na ito?

Hindi, at hindi mo ito dapat gawin. I-bind ang bawat published port sa 127.0.0.1 sa Compose file. Pagkatapos, gamitin ang mga interface sa pamamagitan ng SSH tunnel, VPN, o reverse proxy na nagte-terminate ng TLS (transport layer security) at nagdaragdag ng sarili nitong authentication. Mas mapanganib kaysa sa inaakala ang direktang pag-publish sa mga ito, dahil naglalagay ang Docker ng sarili nitong firewall rules at hindi mapipigilan ng ufw deny rule ang traffic na iyon.

Saan ko makikita ang password ng qBittorrent?

Napi-print ng LinuxServer.io image ang temporary password para sa admin user sa startup log nito. Patakbuhin ang docker compose logs qbittorrent | grep -i password para basahin ito, pagkatapos ay magtakda ng permanenteng password sa ilalim ng Options at Web UI. Gumagawa ng bagong temporary password sa bawat restart hanggang magtakda ka ng sarili mong password.

Magagamit ba ng Jellyfin ang parehong folders?

Oo, at iyon ang layunin ng layout na ito. I-mount ang /mnt/data/media sa media server bilang /media. Mapupunta ang mga library nito sa /media/Movies at /media/Shows, habang nagsusulat ang Sonarr at Radarr sa parehong directories sa pamamagitan ng /data/media. Bigyan ang media server ng parehong PUID at PGID para mabasa nito ang mga file na sinusulat ng arr stack.

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