SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-28

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 utama.

${DATA_ROOT}:/data sama pada ketiga container yang mengakses media. Prowlarr tidak memerlukannya 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 milik Docker akan membawa trafik tersebut langsung melewati aturan deny ufw. Perilaku ini sering mengejutkan pengguna, dan penjelasannya tersedia dalam alasan Docker memublikasikan port langsung melewati ufw.

Port 6881 sengaja dipublikasikan pada semua interface. Port ini adalah port listening torrent dan harus dapat dijangkau agar koneksi peer masuk dapat diterima. Izinkan port tersebut dengan sudo ufw allow 6881, lalu baca dasar-dasar firewall ufw untuk VPS jika perintah itu masih baru bagi Anda.

Direktori konfigurasi dipisahkan untuk setiap aplikasi, sedangkan hanya volume media yang digunakan bersama. Buat direktori tersebut sebelum start pertama agar dimiliki oleh user Anda, bukan oleh 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 Juli 2026, image ini dipublikasikan di lscr.io dan tag latest mengikuti rilis stabil terbaru. Gunakan tag versi tertentu jika Anda ingin upgrade dilakukan berdasarkan keputusan, bukan secara tiba-tiba.

Akses antarmuka web dengan aman

Karena port tersebut berada pada loopback, belum ada yang terekspos. Teruskan port tersebut melalui SSH dari komputer 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 mengakses Sonarr pada server. Untuk akses permanen, tempatkan stack di balik Traefik dengan sertifikat TLS untuk beberapa aplikasi, atau akses server melalui VPN WireGuard yang Anda host sendiri. Jangan menempatkan aplikasi-aplikasi ini di Internet publik dengan hanya halaman login masing-masing sebagai perlindungan. Jika Anda memilih reverse proxy dan lebih memilih satu akun untuk keempat antarmuka daripada harus mengelola empat login aplikasi terpisah, Authentik menyediakan single sign-on yang dapat Anda host sendiri dan dapat diberlakukan oleh Traefik pada setiap permintaan 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 Anda tidak mengubahnya, 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 VPS

Tiga aplikasi arr tersebut ringan. Aplikasi-aplikasi itu melakukan polling terhadap indexer, menulis ke database SQLite kecil, dan mengganti nama file. Server dengan RAM 2 GB dapat menjalankan keempat container dengan nyaman. Beban kerja berasal dari komponen lain. Download client dapat memenuhi kapasitas input dan output disk saat menangani torrent berukuran besar, sedangkan media server yang melakukan transcoding video pada mesin 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 sumber daya untuk hal-hal lain tersebut secara terpisah, bukan dengan menganggap masih tersedia headroom: workspace AFFiNE yang di-hosting sendiri adalah empat container tambahan dengan database di belakangnya, dan pada server dengan RAM 2 GB aplikasi itu akan menggunakan sebagian besar memorinya. Tidak semua service tambahan memerlukan sumber daya sebesar itu: sesuatu yang memiliki satu fungsi seperti pelacak latihan openGym yang di-hosting sendiri dapat berbagi server dengan baik, selama Anda memberinya TLS sendiri dan mengetahui lokasi file databasenya sebelum memercayainya menyimpan riwayat latihan selama setahun. Apa pun yang menjalankan aplikasi web, database Postgres, dan antrean worker latar belakang memerlukan sumber daya yang lebih mendekati kebutuhan AFFiNE. Karena itu, tentukan apakah meja bantuan Chatwoot yang di-hosting sendiri akan ditempatkan di server ini atau di server tersendiri sebelum Anda menemukan batasnya di tengah proses import. Beban kerja yang muncul secara burst memerlukan kehati-hatian lebih besar, karena yang bertabrakan dengan proses import adalah beban puncaknya, bukan rata-ratanya: jika Anda mempertimbangkan OneCLI yang di-hosting sendiri dan memberikan sandbox agent tersendiri kepada setiap orang, bandingkan angka sizing yang dipublikasikannya dengan kapasitas yang benar-benar tersedia saat qBittorrent berjalan pada kapasitas penuh, bukan dengan angka yang ditampilkan 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 dua filesystem 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 tersebut: inode sama dan jumlah link 2.

PUID dan PGID apa yang harus saya gunakan?

Gunakan id numerik akun host yang memiliki media tree. Anda dapat memperoleh nilainya melalui 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 saya perlu mengekspos antarmuka web ini ke Internet?

Tidak, dan sebaiknya jangan. Bind setiap port yang dipublikasikan ke 127.0.0.1 dalam file Compose, lalu akses antarmuka tersebut melalui SSH tunnel, VPN, atau reverse proxy yang melakukan TLS termination (transport layer security) dan menambahkan autentikasi sendiri. Mempublikasikannya secara langsung lebih berisiko daripada yang terlihat karena Docker memasukkan aturan firewall sendiri, dan aturan ufw deny tidak akan menghentikan trafik tersebut.

Di mana saya dapat menemukan password qBittorrent?

Image LinuxServer.io menampilkan password sementara untuk user admin dalam log startup. Jalankan docker compose logs qbittorrent | grep -i password untuk membacanya, lalu tetapkan password permanen melalui Options dan Web UI. Password sementara baru dibuat setiap kali restart sampai Anda menetapkan password sendiri.

Apakah Jellyfin dapat menggunakan folder yang sama?

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

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