SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-10-20

Docker Compose Arr Stack: Prowlarr, Sonarr, Radarr

Jalankan Prowlarr, Sonarr, Radarr, dan qBittorrent dalam satu file Docker Compose di VPS, dengan PUID, PGID, serta tata letak volume agar hardlink berfungsi.

Yang Anda bangun

Stack arr Docker Compose terdiri atas empat container yang mengelola pustaka media: Prowlarr untuk pengaturan indexer, Sonarr untuk serial, Radarr untuk film, dan qBittorrent sebagai klien unduhan. Semua container tersebut berkomunikasi melalui jaringan Compose menggunakan nama service dan berbagi satu struktur folder pada host. Instalasinya singkat. Faktor yang menentukan apakah stack ini dapat berjalan selama bertahun-tahun atau terus menimbulkan masalah setiap minggu adalah tata letak volume. Karena itu, sebagian besar panduan ini membahasnya.

Stack ini tidak mencari konten untuk Anda. Prowlarr menyimpan indexer yang Anda tambahkan, dan keputusan mengenai indexer yang digunakan beserta tanggung jawab hukumnya berada pada Anda. Panduan ini membahas komponen pendukung: user, path, permission, jaringan container, dan pemeriksaan yang membuktikan bahwa semuanya berfungsi.

Jika Anda belum pernah menulis file Compose, baca dasar-dasar Docker Compose untuk VPS terlebih dahulu. Panduan ini mengasumsikan bahwa docker compose version sudah menampilkan sesuatu pada server Anda.

Saat Sonarr selesai mengunduh, Sonarr mengimpor file ke library Anda. Jika folder unduhan dan folder library berada pada filesystem yang sama, proses impor menggunakan hardlink: nama kedua yang menunjuk ke data yang sama pada disk. Proses ini tidak memerlukan ruang tambahan maupun waktu yang berarti. Torrent tetap melakukan seeding dari nama lama, sementara server media membaca nama baru.

Jika kedua folder berada pada filesystem yang berbeda, kernel tidak dapat membuat hardlink tersebut. Sonarr beralih ke proses penyalinan. Satu season berukuran 40 GB kini menggunakan 80 GB ruang disk dan memerlukan beberapa menit untuk operasi input dan output. Log impor juga mencatat bahwa hardlink gagal dan file disalin. Pada VPS dengan kapasitas disk tetap, inilah penyebab ruang disk habis dalam waktu satu minggu.

Inilah masalahnya. Di dalam container, bind mount merupakan batas filesystem. Mount /mnt/data/torrents sebagai /downloads dan /mnt/data/media sebagai /tv. Meskipun keduanya berada pada satu disk host, Sonarr melihatnya sebagai dua mount terpisah dan menolak membuat hardlink di antara keduanya. Dokumentasi image LinuxServer.io resmi menyatakan hal ini secara langsung: penggunaan path /downloads dan /tv secara terpisah menghilangkan kemampuan untuk membuat hardlink.

Solusinya adalah menggunakan satu mount. Setiap container yang mengakses media mendapatkan volume tunggal yang sama, yaitu /mnt/data:/data, dan setiap path yang digunakan harus berupa folder di dalam volume tersebut. Satu mount point, satu filesystem, dan hardlink dapat berfungsi.

Buat pengguna, grup, dan folder

Container menulis file sebagai ID pengguna numerik yang ditetapkan oleh PUID dan PGID. Gunakan akun Anda sendiri agar Anda dapat membaca dan mengedit file tersebut melalui SSH tanpa sudo.

id -u
id -g

Keduanya biasanya menampilkan 1000 pada VPS Ubuntu baru. Sekarang buat struktur direktorinya. Letakkan struktur ini pada disk yang menyimpan media Anda, dan pertahankan seluruh struktur pada disk tersebut.

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

Pastikan struktur tersebut benar-benar berada pada satu filesystem sebelum melanjutkan:

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

Kedua baris harus menampilkan perangkat sumber yang sama. Jika perangkatnya berbeda, hardlink tidak akan pernah berfungsi, apa pun yang Anda tetapkan dalam konfigurasi container.

Folder library sengaja diberi nama Movies dan Shows. Jika Anda sudah menjalankan Jellyfin sebagai server media, mount /mnt/data/media ke Jellyfin sebagai /media, dan library-nya akan berada di /media/Movies dan /media/Shows, tepat seperti yang ditetapkan panduan tersebut.

File lingkungan

Simpan nilai yang berbeda untuk setiap server di .env, di samping file Compose.

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

Tulis ~/arr/.env:

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

Atur TZ ke zona Anda sendiri, misalnya Europe/Berlin. Aplikasi arr menjadwalkan tugas dan mencatat baris log menggunakan zona tersebut. Nilai yang salah akan membuat semua log membingungkan nantinya.

File Compose

Tulis ~/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

Empat hal dalam file tersebut menjalankan fungsi penting.

${DATA_ROOT}:/data identik pada ketiga container yang mengakses media. Prowlarr tidak mendapatkannya karena Prowlarr tidak pernah membuka file media.

Setiap port web di-bind ke 127.0.0.1, sehingga Docker hanya memublikasikannya pada alamat loopback. 8989:8989 biasa akan memublikasikannya pada semua interface, dan aturan firewall Docker sendiri akan meneruskan trafik tersebut langsung melewati aturan deny ufw. Perilaku ini sering mengejutkan pengguna, dan penjelasannya tersedia di alasan Docker memublikasikan port langsung melewati ufw.

Port 6881 sengaja dipublikasikan pada semua interface. Port ini digunakan untuk mendengarkan koneksi torrent dan harus dapat dijangkau agar peer dapat membuat koneksi masuk. Perilaku Docker yang sama berlaku di sini, sehingga Anda tidak perlu membuat aturan ufw untuk port tersebut. Menambahkan aturan tidak akan mengubah apa pun. Dokumentasi Docker, yang diperiksa pada Oktober 2026, menyatakan bahwa trafik menuju port yang dipublikasikan dirutekan sebelum mencapai chain yang digunakan ufw. Karena itu, sudo ufw allow 6881 tidak berpengaruh pada port ini. Port tersebut dapat dijangkau dari Internet segera setelah container dijalankan, dan deny ufw juga tidak akan menutupnya. Jika Anda perlu membatasi pihak yang dapat terhubung, dokumentasi Docker mengarahkan Anda untuk menggunakan aturan pada chain DOCKER-USER, bukan ufw. Gunakan ufw untuk service yang tidak berjalan di Docker, seperti SSH. Jika tool tersebut masih baru bagi Anda, baca dasar-dasar firewall ufw untuk VPS.

Direktori konfigurasi dipisahkan untuk setiap aplikasi, dan hanya volume media yang digunakan bersama. Buat direktori tersebut sebelum start pertama agar kepemilikannya berada pada user Anda, bukan root:

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

Keempat service harus membaca running. Per Oktober 2026, image ini dipublikasikan di lscr.io, dan tag latest mengikuti rilis stabil saat ini. Gunakan tag versi tertentu jika Anda ingin upgrade dilakukan berdasarkan keputusan, bukan terjadi tanpa diduga.

Akses antarmuka web dengan aman

Karena port terikat ke loopback, belum ada layanan yang terekspos. Teruskan port tersebut melalui SSH dari mesin Anda sendiri:

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

Sekarang http://127.0.0.1:8989 di browser Anda membuka Sonarr pada server. Untuk akses permanen, letakkan stack di balik Traefik dengan sertifikat TLS untuk beberapa aplikasi, atau akses server melalui VPN WireGuard yang Anda host sendiri. Namun, VPN tidak dapat menjangkau port tersebut dengan sendirinya. Dokumentasi Docker, yang diperiksa pada Oktober 2026, menyatakan bahwa jika port dipublikasikan pada alamat localhost, hanya Docker host yang dapat mengaksesnya. Untuk menggunakan VPN, tetap jalankan tunnel SSH melalui VPN tersebut, atau ubah binding dalam file Compose dari 127.0.0.1 menjadi alamat milik server pada interface VPN. Tidak satu pun aplikasi ini boleh ditempatkan di Internet publik hanya dengan halaman login masing-masing sebagai perlindungan. Jika Anda memilih reverse proxy dan menginginkan satu akun untuk keempat antarmuka, bukan empat login aplikasi terpisah yang harus dikelola, Authentik menyediakan single sign-on yang di-host sendiri yang dapat diberlakukan Traefik pada setiap request melalui forward auth.

qBittorrent membuat password administrator acak saat pertama kali dijalankan dan mencetaknya ke log container. Baca password tersebut, lalu ubah melalui antarmuka web:

docker compose logs qbittorrent | grep -i password

Jika perubahan ini dilewati, password acak baru akan dibuat setiap kali restart, sehingga Anda harus kembali memeriksa log setiap kali.

Tetapkan path di setiap aplikasi

Di qBittorrent, buka Options, lalu Downloads, dan tetapkan path penyimpanan default ke /data/torrents. Simpan folder unduhan yang belum selesai di dalam tree yang sama, misalnya /data/torrents/incomplete. Unduhan yang selesai di lokasi mana pun di luar /data tidak dapat dibuatkan hard link ke library.

Di Sonarr, buka Settings, lalu Media Management, dan tambahkan root folder /data/media/Shows. Di Radarr, root foldernya adalah /data/media/Movies. Path tersebut berada di dalam container. Host path /mnt/data/media/Shows ditolak karena direktori itu tidak ada dari sudut pandang container.

Di Sonarr dan Radarr, buka Settings, lalu Download Clients, dan tambahkan qBittorrent. Host-nya adalah qbittorrent dan port-nya adalah 8080. Nama service dapat digunakan sebagai hostname karena Compose menempatkan keempat container pada satu network dengan service DNS internal (domain name system). Jangan gunakan localhost di sini: di dalam container Sonarr, localhost adalah Sonarr.

Biarkan Remote Path Mappings tetap kosong. Fitur tersebut digunakan untuk menerjemahkan path yang dilaporkan oleh download client menjadi path yang dapat dilihat oleh aplikasi arr. Dengan satu mount /data yang digunakan bersama, kedua container sudah menggunakan path yang sama. Ini merupakan alasan kedua mengapa layout ini layak diterapkan.

Hubungkan Prowlarr ke Sonarr dan Radarr

Prowlarr meneruskan definisi indexer ke aplikasi lain, sehingga Anda cukup mengonfigurasi indexer satu kali, bukan dua kali. Prowlarr memerlukan kunci API (antarmuka pemrograman aplikasi) dari masing-masing aplikasi.

Di Sonarr, buka Settings, lalu General, dan salin API key. Di Prowlarr, buka Settings, lalu Apps, tambahkan aplikasi Sonarr, dan isi tiga kolom. Prowlarr Server adalah http://prowlarr:9696. Sonarr Server adalah http://sonarr:8989. API Key adalah nilai yang Anda salin. Tekan Test. Hasil berwarna hijau berarti Prowlarr berhasil terhubung ke Sonarr melalui jaringan Compose. Ulangi langkah ini untuk Radarr pada http://radarr:7878.

Hasil berwarna merah yang menyatakan koneksi ditolak hampir selalu berarti nama service salah atau awalan http:// tidak ada. Pastikan nama tersebut dapat di-resolve dari dalam container:

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

Kode status HTTP membuktikan bahwa jalur jaringan berfungsi. Error resolusi nama membuktikan bahwa nama service salah.

Jangan menganggap konfigurasi berhasil sebelum Anda melihat jumlah link. Setelah satu item diimpor, bandingkan file yang diunduh dengan file di library:

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

Angka pertama adalah inode dan angka kedua adalah jumlah link. File yang menggunakan hardlink memiliki inode yang sama di kedua lokasi dan jumlah link sebesar 2. Dua inode berbeda, masing-masing dengan jumlah link 1, berarti Sonarr menyalin file tersebut. Log import akan menyatakan bahwa pembuatan hardlink gagal.

Pantau juga penggunaan disk. df -h /mnt/data seharusnya hampir tidak berubah saat import berlangsung, karena hardlink hanya menambahkan nama tanpa menyalin data.

Yang sebenarnya menyebabkan masalah

Error izin saat import berarti user id container tidak dapat menulis ke folder library. Pesannya adalah Access to the path ... is denied. Periksa dengan ls -ln /mnt/data/media apakah owner id sesuai dengan PUID Anda, dan ingat bahwa direktori memerlukan bit execute sebelum container dapat memasukinya.

File yang tampak dimiliki oleh root berarti container dijalankan sebelum direktori host tersedia, sehingga Docker membuatnya sebagai root. Hentikan stack, chown direktori tersebut, lalu jalankan kembali stack.

Jika menghapus torrent dari qBittorrent menyebabkan file library ikut hilang, berarti import dilakukan dengan menyalin file yang kemudian dihapus, atau Anda menghapus data, bukan entri torrent. Pada hardlink yang benar, menghapus salah satu nama tidak menghapus nama lainnya karena data hanya dibebaskan ketika jumlah link mencapai nol.

Disk yang terisi lebih cepat daripada jumlah media yang Anda tambahkan merupakan bentuk paling mahal dari masalah penyalinan. Jalankan pemeriksaan stat di atas sebelum membeli storage tambahan.

Kebutuhan stack ini dari sebuah VPS

Ketiga aplikasi arr tersebut ringan. Aplikasi-aplikasi itu melakukan polling ke indexer, menulis ke database SQLite kecil, dan mengganti nama file. Server dengan RAM 2 GB dapat menjalankan keempat container dengan nyaman. Beban utama berasal dari layanan lain. Download client dapat memenuhi kapasitas input dan output disk saat menangani torrent berukuran besar, sedangkan media server yang melakukan transcoding video pada server yang sama akan menggunakan CPU. Simpan media pada volume dengan throughput nyata, dan tetapkan batas bandwidth pada download client jika server juga menjalankan hal lain yang penting bagi Anda. Alokasikan anggaran untuk kebutuhan lain tersebut secara terpisah. Jangan menganggap kapasitas cadangan selalu tersedia: workspace AFFiNE yang di-self-host terdiri atas empat container lain dengan database di belakangnya, dan pada server dengan RAM 2 GB layanan ini akan menggunakan sebagian besar memorinya. Tidak semua layanan tambahan membutuhkan sumber daya sebesar itu. Layanan dengan satu fungsi, seperti pelacak latihan openGym yang di-self-host, dapat berbagi server dengan baik, selama Anda memberinya TLS sendiri dan mengetahui lokasi file databasenya sebelum mempercayakan riwayat latihan selama setahun kepadanya. Layanan yang menjalankan aplikasi web, database Postgres, dan antrean worker latar belakang berada lebih dekat ke kisaran AFFiNE. Karena itu, tentukan apakah meja bantuan Chatwoot yang di-self-host akan ditempatkan pada server ini atau pada server tersendiri sebelum Anda menemukan batasnya di tengah proses import. Beban kerja yang muncul secara tiba-tiba memerlukan kehati-hatian lebih besar, karena yang berbenturan dengan proses import adalah beban puncaknya, bukan rata-ratanya. Jika Anda mempertimbangkan OneCLI yang di-self-host untuk memberikan agent terisolasi kepada setiap orang, bandingkan angka sizing yang dipublikasikannya dengan kapasitas yang benar-benar tersedia saat qBittorrent berjalan pada beban penuh, bukan dengan kapasitas yang ditunjukkan free -h pada server yang sedang idle.

FAQ

Karena dari sudut pandang container, sumber dan tujuan berada pada filesystem yang berbeda. Dua bind mount terpisah, seperti /downloads dan /tv, dianggap sebagai filesystem yang berbeda meskipun keduanya berasal dari satu disk host. Mount satu direktori induk sebagai /data di setiap container, lalu letakkan direktori unduhan dan library di dalamnya agar link dapat dibuat. Konfirmasikan hasilnya dengan stat -c '%i %h %n' pada kedua file: inode yang sama dan jumlah link sebesar 2.

PUID dan PGID apa yang harus saya gunakan?

Gunakan ID numerik akun host yang memiliki pohon media. Anda dapat melihatnya dengan id -u dan id -g. Pada Ubuntu VPS baru, nilainya biasanya 1000 untuk keduanya. Setiap container dalam stack harus menggunakan pasangan yang sama. Jika tidak, satu aplikasi dapat menulis file yang tidak dapat dimodifikasi oleh aplikasi lain. Setelah mengubah nilainya, buat ulang container dengan docker compose up -d --force-recreate, lalu perbaiki file yang sudah ada dengan chown -R.

Apakah antarmuka web ini perlu diekspos ke Internet?

Tidak, dan sebaiknya jangan. Bind setiap port yang dipublikasikan ke 127.0.0.1 dalam file Compose, lalu akses antarmuka tersebut melalui tunnel SSH atau reverse proxy pada server. Reverse proxy itu menangani TLS (keamanan lapisan transport) dan menambahkan autentikasinya sendiri. VPN saja tidak dapat mengakses port yang di-bind ke 127.0.0.1 karena Docker membuatnya hanya dapat dijangkau dari host itu sendiri. Jika ingin mengaksesnya langsung melalui VPN, publikasikan port pada alamat VPN server. Mempublikasikan port langsung pada antarmuka publik lebih berisiko daripada yang terlihat karena Docker memasukkan aturan firewall-nya sendiri, dan aturan deny dari ufw tidak akan menghentikan trafik tersebut.

Di mana saya dapat menemukan kata sandi qBittorrent?

Image LinuxServer.io mencetak kata sandi sementara untuk user admin dalam log startup-nya. Jalankan docker compose logs qbittorrent | grep -i password untuk membacanya, lalu tetapkan kata sandi permanen di bagian Options dan Web UI. Kata sandi sementara baru dibuat setiap kali restart sampai Anda menetapkan kata sandi sendiri.

Apakah Jellyfin dapat menggunakan folder yang sama?

Ya, dan itulah tujuan tata letak ini. Mount /mnt/data/media ke server media Anda sebagai /media. Library-nya berada di /media/Movies dan /media/Shows, sementara Sonarr dan Radarr menulis ke direktori yang sama melalui /data/media. Berikan server media pasangan PUID dan PGID yang sama agar dapat membaca file yang ditulis oleh stack arr.

Apakah artikel ini bermanfaat?

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