Arr Stack dalam Satu File Docker Compose
Jalankan Prowlarr, Sonarr, Radarr, dan qBittorrent di VPS dengan satu file Docker Compose, PUID, PGID, serta tata letak volume bersama agar hardlink tetap berfungsi.
Yang Anda bangun
Docker Compose arr stack terdiri dari empat container yang mengelola pustaka media: Prowlarr untuk pengaturan indexer, Sonarr untuk serial, Radarr untuk film, dan qBittorrent sebagai klien unduhan. Container-container tersebut berkomunikasi melalui jaringan Compose menggunakan nama layanan dan berbagi satu struktur folder di host. Instalasinya singkat. Tata letak volume menentukan apakah stack ini dapat berfungsi selama bertahun-tahun atau terus menimbulkan masalah setiap minggu. Karena itu, sebagian besar panduan ini membahas tata letak tersebut.
Stack ini tidak mencari konten untuk Anda. Prowlarr menyimpan indexer yang Anda tambahkan, dan keputusan tentang indexer yang digunakan beserta tanggung jawab hukumnya berada pada Anda. Panduan ini membahas konfigurasi pendukung: pengguna, path, izin, jaringan container, dan pemeriksaan untuk memastikan semuanya berfungsi.
Jika Anda belum pernah menulis file Compose, baca dasar-dasar Docker Compose untuk VPS terlebih dahulu. Panduan ini mengasumsikan docker compose version sudah menampilkan sesuatu di server Anda.
Mengapa hardlink gagal, dan mengapa inilah inti persoalannya
Setelah Sonarr selesai mengunduh, Sonarr mengimpor file ke pustaka Anda. Jika folder unduhan dan folder pustaka 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 atau waktu tambahan. 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 link tersebut. Sonarr beralih ke penyalinan. Season berukuran 40 GB kini menggunakan 80 GB ruang disk dan memerlukan beberapa menit operasi input dan output. Log impor mencatat bahwa hardlink gagal dan file disalin sebagai gantinya. Pada VPS dengan batas ruang disk tetap, inilah yang dapat menyebabkan ruang habis dalam waktu seminggu.
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 melihat dua mount terpisah dan menolak membuat link di antara keduanya. Dokumentasi image LinuxServer.io resmi menyatakan hal ini secara langsung: penggunaan path /downloads dan /tv secara terpisah menghilangkan kemampuan untuk menggunakan hardlink.
Solusinya adalah satu mount. Setiap container yang mengakses media mendapatkan volume tunggal yang sama, yaitu /mnt/data:/data, dan setiap path yang digunakan berada di dalamnya. Satu titik mount, satu filesystem, dan hardlink berfungsi.
Buat pengguna, grup, dan folder
Container menulis file menggunakan 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 -gKeduanya biasanya menampilkan 1000 pada Ubuntu VPS yang baru dibuat. Sekarang buat struktur direktorinya. Letakkan struktur tersebut di disk yang menyimpan media Anda, dan pastikan seluruh struktur berada di satu disk itu.
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/dataPastikan struktur tersebut benar-benar berada pada satu filesystem sebelum melanjutkan:
df --output=source,target /mnt/data/torrents /mnt/data/mediaKedua baris harus menampilkan perangkat sumber yang sama. Dua perangkat yang berbeda berarti hardlink tidak akan pernah berfungsi, apa pun yang Anda tetapkan dalam konfigurasi container.
Folder pustaka sengaja diberi nama Movies dan Shows. Jika Anda sudah menjalankan Jellyfin sebagai server media, mount /mnt/data/media ke Jellyfin sebagai /media, dan pustakanya 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 sebelah file Compose.
mkdir -p ~/arr && cd ~/arrTulis ~/arr/.env:
PUID=1000
PGID=1000
TZ=Etc/UTC
DATA_ROOT=/mnt/dataTetapkan TZ ke zona Anda sendiri, seperti Europe/Berlin. Aplikasi arr menjadwalkan tugas dan memberi cap waktu pada baris log berdasarkan zona tersebut. Nilai yang salah akan membuat semua log sulit dipahami 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-stoppedAda empat hal dalam file tersebut yang menjalankan fungsi penting.
${DATA_ROOT}:/data sama pada ketiga container yang mengakses media. Prowlarr tidak memerlukannya karena Prowlarr tidak pernah membuka file media.
Setiap port web diikat 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 traffic tersebut langsung melewati aturan deny ufw. Perilaku ini sering mengejutkan pengguna. Penjelasannya tersedia dalam alasan Docker memublikasikan port langsung melewati ufw.
Port 6881 sengaja dipublikasikan pada semua interface. Port tersebut adalah port listening torrent dan harus dapat dijangkau untuk koneksi peer masuk. Izinkan port tersebut dengan sudo ufw allow 6881. Jika perintah itu masih baru bagi Anda, baca dasar-dasar firewall ufw untuk VPS.
Direktori konfigurasi dipisahkan untuk setiap aplikasi. Hanya volume media yang digunakan bersama. Buat direktori tersebut sebelum menjalankan layanan untuk pertama kali agar direktori 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 psKeempat layanan harus membaca running. Per Juli 2026, image ini dipublikasikan di lscr.io, dan tag latest mengikuti rilis stabil saat ini. Gunakan tag versi tertentu jika Anda ingin menjadikan upgrade sebagai keputusan, bukan kejutan.
Akses antarmuka web dengan aman
Karena port tersebut berada pada loopback, belum ada layanan 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-serverSekarang http://127.0.0.1:8989 di browser Anda mengakses Sonarr pada server. Untuk akses permanen, tempatkan stack di belakang Traefik dengan sertifikat TLS untuk beberapa aplikasi, atau akses server melalui VPN WireGuard yang Anda kelola sendiri. Jangan menempatkan aplikasi-aplikasi ini di internet publik hanya dengan halaman login masing-masing sebagai perlindungan.
qBittorrent membuat kata sandi administrator acak saat pertama kali dijalankan dan menampilkannya di log container. Baca kata sandi tersebut, lalu ubah melalui antarmuka web:
docker compose logs qbittorrent | grep -i passwordJika Anda melewatkan perubahan ini, kata sandi acak baru akan dibuat setiap kali container dimulai ulang, sehingga Anda harus kembali memeriksa log setiap kali.
Tetapkan path di dalam 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 dalam pustaka.
Di Sonarr, buka Settings, lalu Media Management, dan tambahkan root folder /data/media/Shows. Di Radarr, root folder-nya adalah /data/media/Movies. Path ini berada di dalam container. Path host /mnt/data/media/Shows ditolak karena direktori tersebut 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 (domain name system) internal. Jangan gunakan localhost di sini: di dalam container Sonarr, localhost adalah Sonarr.
Biarkan Remote Path Mappings kosong. Fitur ini 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 adalah alasan kedua mengapa tata letak ini sepadan dengan upaya tersebut.
Menghubungkan Prowlarr ke Sonarr dan Radarr
Prowlarr meneruskan definisi indexer ke aplikasi lain. Dengan demikian, Anda mengonfigurasi indexer satu kali, bukan dua kali. Prowlarr memerlukan kunci API (application programming interface) dari setiap aplikasi.
Di Sonarr, buka Settings, lalu General, dan salin kunci API. Di Prowlarr, buka Settings, lalu Apps, tambahkan aplikasi Sonarr, dan isi tiga bidang. 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 di http://radarr:7878.
Hasil berwarna merah yang menyatakan bahwa koneksi ditolak hampir selalu berarti nama layanan salah atau prefiks 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:8989Kode status HTTP membuktikan bahwa jalur jaringan berfungsi. Kesalahan resolusi nama membuktikan bahwa nama layanan salah.
Buktikan bahwa hardlink benar-benar dibuat
Jangan mempercayai konfigurasi sebelum Anda melihat jumlah tautan. Setelah satu item diimpor, bandingkan file yang diunduh dengan file di pustaka:
stat -c '%i %h %n' /mnt/data/torrents/tv/*/*.mkv
stat -c '%i %h %n' /mnt/data/media/Shows/*/*/*.mkvAngka pertama adalah inode dan angka kedua adalah jumlah tautan. File yang dibuat sebagai hardlink memiliki inode yang sama di kedua lokasi dan jumlah tautan sebesar 2. Dua inode berbeda, masing-masing dengan jumlah tautan 1, berarti Sonarr menyalin file. Log impor akan menyatakan bahwa pembuatan hardlink gagal.
Pantau penggunaan disk juga. df -h /mnt/data seharusnya hampir tidak berubah saat impor berlangsung, karena hardlink hanya menambahkan nama dan tidak menambahkan data.
Penyebab sebenarnya
Kesalahan izin saat impor berarti ID pengguna container tidak dapat menulis ke folder pustaka. Pesannya adalah Access to the path ... is denied. Periksa dengan ls -ln /mnt/data/media bahwa ID pemilik cocok dengan PUID Anda. Ingat bahwa direktori memerlukan bit eksekusi agar container dapat memasukinya.
File yang tampak dimiliki oleh root berarti container dimulai sebelum direktori host tersedia, sehingga Docker membuatnya sebagai root. Hentikan stack, chown direktori tersebut, lalu mulai kembali.
Jika menghapus torrent dari qBittorrent menyebabkan file pustaka ikut hilang, berarti impor dilakukan dengan menyalin file lalu salinannya dihapus, atau Anda menghapus datanya, bukan entri torrent. Pada hardlink yang sebenarnya, menghapus salah satu nama tidak menghapus nama lainnya karena data hanya dibebaskan ketika jumlah link mencapai nol.
Jika disk terisi lebih cepat daripada jumlah media yang Anda tambahkan, masalah penyalinan sedang terjadi dalam bentuk yang paling boros. Jalankan pemeriksaan stat di atas sebelum membeli penyimpanan tambahan.
Kebutuhan stack ini dari VPS
Tiga aplikasi arr tidak membutuhkan banyak sumber daya. Aplikasi tersebut melakukan polling pada indexer, menulis ke database SQLite kecil, dan mengganti nama file. Server dengan RAM 2 GB dapat menjalankan keempat container dengan nyaman. Beban kerja utama berasal dari komponen lain. Klien download dapat memenuhi kapasitas input dan output disk saat menangani torrent berukuran besar. Server media yang melakukan transcoding video pada server yang sama juga akan membebani CPU. Simpan media pada volume dengan throughput nyata. Tetapkan batas bandwidth pada klien download jika server juga menjalankan layanan lain yang penting.
FAQ
Mengapa Sonarr menyalin file, bukan membuat hard link?
Karena dari sudut pandang container, sumber dan tujuan berada pada filesystem yang berbeda. Dua bind mount terpisah, seperti /downloads dan /tv, merupakan dua filesystem meskipun keduanya berasal dari satu disk host. Mount satu direktori induk sebagai /data di setiap container, lalu letakkan direktori unduhan dan pustaka 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. Nilai tersebut dapat diperoleh dengan id -u dan id -g. Pada Ubuntu VPS baru, biasanya keduanya adalah 1000. Setiap container dalam stack harus menggunakan pasangan yang sama. Jika tidak, satu aplikasi dapat menulis file yang tidak dapat diubah oleh aplikasi lain. Setelah mengubah nilainya, buat ulang container dengan docker compose up -d --force-recreate dan perbaiki file yang 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, VPN, atau reverse proxy yang menghentikan TLS (keamanan lapisan transport) dan menambahkan autentikasinya sendiri. Mempublikasikannya secara langsung lebih berisiko daripada yang terlihat, karena Docker memasukkan aturan firewall-nya sendiri dan aturan deny ufw tidak akan menghentikan traffic tersebut.
Di mana saya dapat menemukan kata sandi qBittorrent?
Image LinuxServer.io mencetak kata sandi sementara untuk pengguna 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 sebagai /media. Pustakanya berada di /media/Movies dan /media/Shows, sedangkan Sonarr dan Radarr menulis ke direktori yang sama melalui /data/media. Berikan server media PUID dan PGID yang sama agar dapat membaca file yang ditulis oleh stack arr.