Tek Docker Compose Dosyası ile Arr Yığını Kurulumu
Prowlarr, Sonarr, Radarr ve qBittorrent servislerini tek bir Docker Compose dosyasında yapılandırın. Hardlink hatalarını önleyen PUID, PGID ve volume dizin ayarlarını öğrenin.
Ne inşa ediyorsunuz
Docker Compose tabanlı bir arr yığını, medya kütüphanesini yöneten dört container'dan oluşur: indeksleyici ayarları için Prowlarr, diziler için Sonarr, filmler için Radarr ve indirme istemcisi olarak qBittorrent. Bu servisler, Compose ağı üzerinde servis adları aracılığıyla birbirleriyle haberleşir ve ana makine üzerinde tek bir klasör ağacını paylaşırlar. Kurulum kısa sürer. Yığının yıllarca sorunsuz çalışmasını mı yoksa her hafta hata vermesini mi belirleyen temel unsur birim (volume) düzenidir; bu nedenle rehberin büyük bir kısmı bunun üzerinedir.
Bu yığın sizin yerinize içerik bulmaz. Prowlarr, içine eklediğiniz indeksleyicileri barındırır; hangi indeksleyicileri kullanacağınız sizin kararınız ve yasal sorumluluğunuzdur. Bu rehber altyapıyı ele alır: kullanıcılar, yollar, izinler, container ağ iletişimi ve sistemin çalıştığını doğrulayan kontroller.
Daha önce hiç Compose dosyası yazmadıysanız, önce VPS için Docker Compose temelleri başlıklı yazıyı okuyun. Bu yazı, docker compose version komutunun sunucunuzda halihazırda bir çıktı verdiğini varsayar.
Hardlink'ler neden bozulur ve neden tüm mesele budur
Sonarr bir indirme işlemini tamamladığında, dosyayı kütüphanenize aktarır. İndirme klasörü ve kütüphane klasörü aynı dosya sistemi üzerinde bulunuyorsa, aktarım işlemi bir hardlink olarak gerçekleşir: bu, diskteki aynı veriyi işaret eden ikinci bir isimdir. Bu işlem ek disk alanı kaplamaz ve zaman almaz. Torrent eski isim üzerinden seed edilmeye devam ederken, medya sunucunuz yeni isim üzerinden okuma yapar.
Eğer iki klasör farklı dosya sistemleri üzerindeyse, kernel bu bağlantıyı oluşturamaz. Sonarr bu durumda kopyalama yöntemine geri döner. 40 GB boyutundaki bir sezon artık 80 GB disk alanı kaplar ve birkaç dakikalık giriş/çıkış (I/O) işlemine neden olur; aktarım günlüğü, hardlink işleminin başarısız olduğunu ve dosyanın kopyalandığını kaydeder. Sabit disk kotası olan bir VPS üzerinde, kullanıcıların bir hafta içinde disk alanının tükenmesinin sebebi budur.
İşte tuzak burada. Bir container içinde, bind mount bir dosya sistemi sınırıdır. /mnt/data/torrents yolunu /downloads olarak, /mnt/data/media yolunu ise /tv olarak mount ederseniz, her ikisi de aynı ana makine diski üzerinde olsa bile, Sonarr bunları iki ayrı mount noktası olarak görür ve aralarında link oluşturmayı reddeder. Resmi LinuxServer.io imaj dokümantasyonu bunu doğrudan belirtir: ayrı /downloads ve /tv yollarını kullanmak, hardlink yeteneğinden feragat etmek anlamına gelir.
Çözüm tek bir mount kullanmaktır. Medya ile etkileşime giren her container aynı tekil volume olan /mnt/data:/data'i alır ve kullandıkları her yol bunun içindeki bir klasördür. Tek bir mount noktası, tek bir dosya sistemi ve çalışan hardlink'ler.
Kullanıcıyı, grubu ve klasörleri oluşturma
Konteynerler dosyaları, PUID ve PGID ile belirlenen sayısal bir kullanıcı kimliği ile yazar. Dosyaları SSH üzerinden sudo kullanmadan okuyabilmek ve düzenleyebilmek için kendi hesabınızı kullanın.
id -u
id -gHer ikisi de yeni bir Ubuntu VPS üzerinde genellikle 1000 çıktısını verir. Şimdi dizin ağacını oluşturun. Bu ağacı medya dosyalarınızı barındıran diske yerleştirin ve tüm ağacı aynı disk üzerinde tutun.
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İlerlemeden önce bunun gerçekten tek bir dosya sistemi olduğunu doğrulayın:
df --output=source,target /mnt/data/torrents /mnt/data/mediaHer iki satır da aynı kaynak aygıtı göstermelidir. İki farklı aygıt, konteyner yapılandırmasında ne ayarlarsanız ayarlayın hardlink'lerin hiçbir zaman çalışmayacağı anlamına gelir.
Kütüphane klasörleri, Movies ve Shows olarak kasten adlandırılmıştır. Eğer halihazırda medya sunucusu olarak Jellyfin çalıştırıyorsanız, /mnt/data/media dizinini Jellyfin içine /media olarak bağlayın; böylece kütüphaneleriniz, ilgili rehberde belirtildiği gibi tam olarak /media/Movies ve /media/Shows konumlarına yerleşecektir.
Ortam dosyası
Sunucudan sunucuya değişen değerleri, Compose dosyasının yanında bulunan .env içerisinde tutun.
mkdir -p ~/arr && cd ~/arr~/arr/.env dosyasını oluşturun:
PUID=1000
PGID=1000
TZ=Etc/UTC
DATA_ROOT=/mnt/dataTZ değerini, Europe/Berlin gibi kendi zaman diliminize ayarlayın. Arr uygulamaları görevleri bu zaman dilimine göre planlar ve log satırlarını bu zaman dilimiyle damgalar; yanlış bir değer, ileride tüm logların kafa karıştırıcı olmasına neden olur.
Compose dosyası
~/arr/docker-compose.yml dosyasını oluşturun:
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-stoppedBu dosyadaki dört öğe fiili işi gerçekleştirmektedir.
${DATA_ROOT}:/data, medya dosyalarına erişen üç container içinde aynıdır. Prowlarr hiçbir medya dosyasını açmadığı için bu ayarı almaz.
Tüm web portları 127.0.0.1 adresine bağlıdır; bu sayede Docker bunları yalnızca loopback adresi üzerinden yayınlar. Yalın bir 8989:8989 kullanımı, portları tüm arayüzlerde yayınlayacaktır ve Docker'ın kendi güvenlik duvarı kuralları, trafiği doğrudan ufw deny kuralının üzerinden geçirecektir. Bu davranış kullanıcılar için sürekli bir sürpriz oluşturur; konuyla ilgili ayrıntılar Docker'ın portları neden ufw üzerinden doğrudan yayınladığı kısmında açıklanmıştır.
6881 numaralı port, tüm arayüzlerde bilerek yayınlanmıştır. Bu, torrent dinleme portudur ve gelen eş (peer) bağlantıları için erişilebilir olması gerekir. sudo ufw allow 6881 ile bu porta izin verin; eğer bu komut sizin için yeniyse VPS için ufw güvenlik duvarı temelleri kısmını okuyun.
Yapılandırma dizinleri her uygulama için ayrıdır ve yalnızca medya birimi (volume) paylaşılmaktadır. İlk başlatmadan önce bu dizinleri oluşturun; böylece dizinlerin sahibi root yerine sizin kullanıcınız olur:
mkdir -p ~/arr/config/prowlarr ~/arr/config/sonarr ~/arr/config/radarr ~/arr/config/qbittorrent
docker compose up -d
docker compose psDört servisin tamamı running değerini okumalıdır. Temmuz 2026 itibarıyla bu imajlar lscr.io üzerinde yayınlanmaktadır ve latest etiketi güncel kararlı sürümü takip eder. Güncellemelerin sürpriz olmasını istemiyor, bilinçli bir karar olmasını tercih ediyorsanız bunun yerine sabit bir sürüm etiketi kullanın.
Web arayüzlerine güvenli erişim
Portlar loopback üzerinde olduğu için henüz dışarıya açık bir servis bulunmamaktadır. Bunları kendi makinenizden SSH üzerinden yönlendirin:
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-serverArtık tarayıcınızda http://127.0.0.1:8989 adresi üzerinden sunucudaki Sonarr arayüzüne erişebilirsiniz. Kalıcı erişim için yığını birden fazla uygulama için TLS sertifikalı Traefik arkasına alın veya sunucuya kendi barındırdığınız bir WireGuard VPN üzerinden bağlanın. Bu uygulamaların hiçbiri, yalnızca kendi giriş sayfalarıyla doğrudan genel internete açık bırakılmamalıdır. Eğer reverse proxy yöntemini seçerseniz ve dört ayrı uygulama girişiyle uğraşmak yerine tüm arayüzler için tek bir hesap kullanmak isterseniz, Authentik ile self-hosted tek oturum açma (SSO) sistemini kurabilir ve Traefik üzerinden her istekte forward auth ile doğrulama yapabilirsiniz.
qBittorrent ilk başlatıldığında rastgele bir yönetici parolası oluşturur ve bunu container günlüğüne (log) yazdırır. Bu parolayı okuyun ve ardından web arayüzünden değiştirin:
docker compose logs qbittorrent | grep -i passwordEğer parola değişikliğini yapmazsanız, her yeniden başlatmada yeni bir rastgele parola oluşturulur ve her seferinde tekrar günlük kayıtlarına bakmak zorunda kalırsınız.
Her uygulama içindeki yolları ayarlayın
qBittorrent içinde Options menüsünü, ardından Downloads sekmesini açın ve varsayılan kayıt yolunu /data/torrents olarak ayarlayın. Tamamlanmamış indirmeler klasörünü, /data/torrents/incomplete gibi aynı dizin ağacı içinde tutun. /data dışında herhangi bir yerde tamamlanan bir indirme, kütüphane içine hardlink ile bağlanamaz.
Sonarr içinde Settings menüsünü, ardından Media Management sekmesini açın ve /data/media/Shows kök klasörünü ekleyin. Radarr içinde kök klasör /data/media/Movies şeklindedir. Bunlar container içindeki yollardır. /mnt/data/media/Shows ana makine yolu reddedilir, çünkü bu dizin container bakış açısından mevcut değildir.
Hem Sonarr hem de Radarr içinde Settings menüsünü, ardından Download Clients sekmesini açın ve qBittorrent'i ekleyin. Ana makine qbittorrent, port ise 8080'dir. Compose, tüm dört container'ı dahili bir DNS (alan adı sistemi) servisi ile aynı ağa dahil ettiği için servis adı, ana makine adı (hostname) olarak çalışır. Burada localhost kullanmayın: Sonarr container'ı içinde localhost, Sonarr'ın kendisidir.
Remote Path Mappings kısmını boş bırakın. Bu özellik, indirme istemcisinin rapor ettiği bir yolu, arr uygulamasının görebileceği bir yola dönüştürmek için vardır. Tek bir paylaşılan /data mount noktası ile her iki container da tüm yollar üzerinde zaten mutabıktır; bu düzenin tercih edilmesinin ikinci nedeni de budur.
Prowlarr'ı Sonarr ve Radarr'a Bağlama
Prowlarr, indeksleyici tanımlarını diğer uygulamalara gönderir; böylece bir indeksleyiciyi iki kez değil, yalnızca bir kez yapılandırırsınız. Bunun için her uygulamadan bir API (application programming interface) anahtarı gerekir.
Sonarr'da Settings, ardından General menüsünü açın ve API anahtarını kopyalayın. Prowlarr'da Settings, ardından Apps menüsünü açın, bir Sonarr uygulaması ekleyin ve üç alanı doldurun. Prowlarr Server http://prowlarr:9696 değerindedir. Sonarr Server http://sonarr:8989 değerindedir. API Key ise kopyaladığınız değerdir. Test düğmesine basın. Yeşil bir sonuç, Prowlarr'ın Compose ağı üzerinden Sonarr'a ulaştığı anlamına gelir. Aynı işlemi http://radarr:7878 adresindeki Radarr için tekrarlayın.
Bağlantının reddedildiğini belirten kırmızı bir sonuç, neredeyse her zaman yanlış bir servis adına veya eksik bir http:// önekine işaret eder. İsmin container içinden çözümlendiğini doğrulayın:
docker compose exec prowlarr curl -sS -o /dev/null -w '%{http_code}\n' http://sonarr:8989Bir HTTP durum kodu, ağ yolunun düzgün çalıştığını kanıtlar. İsim çözümleme hatası ise servis adının yanlış olduğunu kanıtlar.
Hardlink işleminin gerçekleştiğini doğrulama
Bağlantı sayısını (link count) görmeden kuruluma güvenmeyin. Bir öğe içe aktarıldıktan sonra, indirilen dosya ile kütüphane dosyasını karşılaştırın:
stat -c '%i %h %n' /mnt/data/torrents/tv/*/*.mkv
stat -c '%i %h %n' /mnt/data/media/Shows/*/*/*.mkvİlk sayı inode numarasını, ikincisi ise bağlantı sayısını gösterir. Hardlink ile bağlanan bir dosya her iki konumda da aynı inode numarasını ve 2 bağlantı sayısını gösterir. Her biri 1 bağlantı sayısına sahip iki farklı inode, Sonarr'ın dosyayı kopyaladığı anlamına gelir; bu durumda içe aktarma günlüğünde hardlink işleminin başarısız olduğu belirtilir.
Disk kullanımını da izleyin. Bir içe aktarma işlemi gerçekleştiğinde df -h /mnt/data neredeyse hiç değişmemelidir, çünkü hardlink yalnızca bir isim ekler ve veri kopyalamaz.
Neler sorun çıkarır
İçe aktarma sırasındaki izin hataları, container kullanıcısının kimlik numarasının (UID) kütüphane klasörüne yazamadığı anlamına gelir. Hata mesajı Access to the path ... is denied şeklindedir. ls -ln /mnt/data/media komutuyla sahiplik kimliğinin PUID değerinizle eşleştiğini doğrulayın ve dizinlere erişim sağlanabilmesi için dizinlerin çalıştırılabilir (execute) bitine sahip olması gerektiğini unutmayın.
Dosyaların sahibi root olarak görünüyorsa, container ana makine dizini oluşmadan önce başlatılmış demektir; bu durumda Docker dizini root yetkisiyle oluşturur. Stack'i durdurun, dizini chown komutuyla silin ve tekrar başlatın.
qBittorrent üzerinden bir torrent silindiğinde kütüphane dosyasının da kaybolması, içe aktarma işleminin bir kopyalama olduğunu ve daha sonra silindiğini ya da torrent kaydı yerine doğrudan verinin silindiğini gösterir. Gerçek bir hardlink kullanıldığında, isimlerden birinin silinmesi diğerini etkilemez; çünkü veri ancak bağlantı sayısı sıfıra ulaştığında serbest bırakılır.
Diskin eklediğiniz medyadan daha hızlı dolması, kopyalama sorununun en maliyetli halidir. Daha fazla depolama alanı satın almadan önce yukarıdaki stat kontrolünü çalıştırın.
Bu yığının bir VPS'den beklentileri
Üç arr uygulaması hafiftir. Indexer'ları sorgular, küçük bir SQLite veritabanına yazar ve dosyaları yeniden adlandırır. 2 GB RAM'e sahip bir sunucu dört container'ın tümünü rahatça çalıştırır. Yük başka kaynaklardan gelir. Büyük torrent'lerde bir download client disk giriş ve çıkışını tamamen kullanabilir. Aynı makinede video transcode eden bir media server CPU'yu tüketebilir. Media dosyaları gerçek aktarım kapasitesi sunan bir volume üzerinde tutulmalıdır. Sunucuda önem verdiğiniz başka işler de çalışıyorsa download client üzerinde bant genişliği sınırı belirlenmelidir. Bu diğer işler için kapasiteyi ayrıca planlayın. Kullanılabilir ek kapasitenin yeterli olduğunu varsaymayın: self-hosted AFFiNE çalışma alanı arka planda bir veritabanı bulunan dört container daha çalıştırır ve 2 GB RAM'e sahip bir makinede belleğin büyük bölümünü kendisi kullanmak ister. Her ek servis bu kadar kaynak tüketmez. self-hosted openGym antrenman takip uygulaması gibi tek amaçlı bir uygulama, kendi TLS yapılandırması yapılırsa ve bir yıllık antrenman geçmişi emanet edilmeden önce veritabanı dosyasının konumu biliniyorsa aynı makineyi sorunsuz biçimde paylaşabilir. Bir web uygulaması, Postgres veritabanı ve arka plan worker kuyruğu içeren uygulamalar bu aralığın AFFiNE tarafına daha yakındır. Bu nedenle bir import işleminin ortasında sınırı öğrenmeden önce self-hosted Chatwoot destek masasının bu sunucuda mı yoksa ayrı bir sunucuda mı çalıştırılacağına karar verilmelidir. Ani yük oluşturan işlerde daha da dikkatli olunmalıdır. Çünkü import işlemiyle çakışan değer ortalama kullanım değil, tepe kullanımdır: her kişi için kendi sandbox'lanmış agent'ını çalıştıran self-hosted OneCLI değerlendiriliyorsa, yayımlanan boyutlandırma değerleri qBittorrent tam kapasiteyle çalışırken gerçekten boş olan kaynaklarla karşılaştırılmalıdır. Boştaki bir makinede free -h çıktısıyla karşılaştırma yapılmamalıdır.
FAQ
Sonarr neden dosyaları hardlink yapmak yerine kopyalıyor?
Çünkü kaynak ve hedef, container bakış açısından farklı dosya sistemlerindedir. /downloads ve /tv gibi iki ayrı bind mount, her ikisi de aynı ana makine diskinden gelse bile iki ayrı dosya sistemidir. Tek bir üst dizini her container içinde /data olarak bağlayın, indirme ve kütüphane klasörlerini bunun içine yerleştirin; böylece link işlemi mümkün hale gelir. Sonucu her iki dosya üzerinde stat -c '%i %h %n' ile doğrulayın: aynı inode ve 2 değerinde bir link sayısı görmelisiniz.
Hangi PUID ve PGID değerlerini kullanmalıyım?
Medya ağacının sahibi olan ana makine hesabının sayısal kimliğini kullanın; bu değerleri id -u ve id -g komutlarıyla öğrenebilirsiniz. Yeni kurulmuş bir Ubuntu VPS üzerinde bu değerler genellikle her ikisi için de 1000'tür. Stack içindeki her container aynı çifti kullanmalıdır, aksi takdirde bir uygulamanın yazdığı dosyaları diğeri değiştiremez. Değerleri değiştirdikten sonra container'ları docker compose up -d --force-recreate ile yeniden oluşturun ve mevcut dosyaları chown -R ile düzeltin.
Bu web arayüzlerini internete açmam gerekiyor mu?
Hayır, açmamalısınız. Compose dosyasındaki her bir yayınlanmış portu 127.0.0.1 adresine bağlayın; ardından arayüzlere bir SSH tüneli, VPN veya TLS (transport layer security) sonlandırması yapan ve kendi kimlik doğrulamasını ekleyen bir reverse proxy üzerinden erişin. Bunları doğrudan yayınlamak göründüğünden daha tehlikelidir, çünkü Docker kendi güvenlik duvarı kurallarını ekler ve bir ufw deny kuralı bu trafiği durdurmaz.
qBittorrent şifresini nerede bulabilirim?
LinuxServer.io imajı, başlangıç günlüğünde admin kullanıcısı için geçici bir şifre yazdırır. Şifreyi okumak için docker compose logs qbittorrent | grep -i password komutunu çalıştırın, ardından Options ve Web UI altından kalıcı bir şifre belirleyin. Siz kendi şifrenizi belirleyene kadar her yeniden başlatmada yeni bir geçici şifre oluşturulur.
Jellyfin aynı klasörleri kullanabilir mi?
Evet, bu düzenin amacı da budur. /mnt/data/media dizinini medya sunucunuza /media olarak bağlayın; kütüphaneleriniz /media/Movies ve /media/Shows konumlarında yer alırken, Sonarr ve Radarr aynı dizinlere /data/media üzerinden yazma işlemi yapar. Medya sunucusuna da aynı PUID ve PGID değerlerini verin, böylece arr stack'in yazdığı dosyaları okuyabilir.