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 gerçek işi yapmaktadır.
${DATA_ROOT}:/data, medya dosyalarına erişen üç container içinde aynıdır. Prowlarr medya dosyası açmadığı için bu ayarı almaz.
Her web portu 127.0.0.1 adresine bağlıdır; bu sayede Docker, portu yalnızca loopback adresi üzerinden yayınlar. Yalın bir 8989:8989 kullanımı, portu 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ı sürekli şaşırtmaktadır; konu Docker'ın portları neden ufw üzerinden doğrudan yayınladığı kısmında açıklanmıştır.
6881 numaralı port, özellikle tüm arayüzlerde yayınlanır. Bu, torrent dinleme portudur ve gelen eş bağlantıları için erişilebilir olması gerekir. Aynı Docker davranışı burada da geçerlidir; bu nedenle port için bir ufw kuralına ihtiyacınız yoktur ve kural eklemek hiçbir şeyi değiştirmez. Ekim 2026 itibarıyla kontrol edilen Docker belgeleri, yayınlanan bir porta gelen trafiğin ufw tarafından kullanılan zincirlere ulaşmadan önce yönlendirildiğini belirtir; bu yüzden sudo ufw allow 6881 bu port üzerinde etkisizdir. Container başladığı anda port internete açılır ve bir ufw deny kuralı da bunu kapatmaz. Port bağlantılarını kısıtlamanız gerekirse, Docker belgeleri ufw yerine DOCKER-USER zincirindeki kuralları işaret eder. ufw'yi SSH gibi Docker içinde çalışmayan servisler için kullanmaya devam edin; eğer bu araç 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 paylaşılı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 dosyasını okumalıdır. Ekim 2026 itibarıyla bu imajlar lscr.io üzerinde yayınlanmaktadır ve latest etiketi mevcut kararlı sürümü takip eder. Güncellemelerin sürpriz olmasını istemiyor ve bir karar mekanizmasına bağlı kalmasını istiyorsanız, bunun yerine sabit bir sürüm etiketi kullanın.
Web arayüzlerine güvenli bir şekilde erişin
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 ulaşabilirsiniz. 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 erişin. Ancak bir VPN, tek başına bu portlara erişim sağlamaz. Ekim 2026'da kontrol edilen Docker dokümantasyonuna göre, bir port localhost adresi üzerinde yayınlandığında, buna yalnızca Docker ana makinesi erişebilir. VPN kullanmak için ya SSH tünelini VPN üzerinden çalıştırmaya devam edin ya da Compose dosyasındaki bağlamaları 127.0.0.1 yerine sunucunun VPN arayüzündeki kendi adresiyle değiştirin. Bu uygulamaların hiçbiri, yalnızca kendi giriş sayfalarıyla genel internete açık bırakılmamalıdır. Eğer reverse proxy yöntemini seçerseniz ve takip etmeniz gereken dört ayrı uygulama girişi yerine dört arayüz için tek bir hesap kullanmak isterseniz, Authentik, Traefik'in forward auth ile her istekte zorunlu kılabileceği self-hosted tek oturum açma (SSO) imkanı sunar.
qBittorrent ilk başlatıldığında rastgele bir yönetici parolası oluşturur ve bunu container günlüğüne yazdırır. Bu parolayı okuyun ve ardından web arayüzünden değiştirin:
docker compose logs qbittorrent | grep -i passwordEğer bu değişikliği 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 bozulur
İçe aktarma sırasındaki izin hataları, container kullanıcısının kimlik numarasının (user id) 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 sahibin kimlik numarasının sizin PUID değerinizle eşleşip eşleşmediğini kontrol edin ve container'ın dizinlere girebilmesi için dizinlerin execute bitine sahip olması gerektiğini unutmayın.
root tarafından sahiplenilmiş gibi görünen dosyalar, container'ın ana makine dizini henüz mevcut değilken başlatıldığı ve bu nedenle Docker'ın dizini root olarak oluşturduğu anlamına gelir. Stack'i durdurun, dizinin sahipliğini chown komutuyla değiştirin 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 sonradan silindiğini ya da torrent kaydı yerine verinin kendisini sildiğinizi gösterir. Gerçek bir hardlink kullanıldığında, isimlerden birinin kaldırılması diğerini etkilemez; çünkü veri, yalnızca 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. İndeksleyicileri sorgular, küçük bir SQLite veritabanına yazar ve dosyaları yeniden adlandırırlar. 2 GB RAM'e sahip bir sunucu, dört container'ı da rahatlıkla çalıştırır. Yük başka yerlerden gelir. Bir indirme istemcisi, büyük torrentlerde disk giriş ve çıkışını doyurur; aynı makinede video kod dönüştürme (transcoding) yapan bir medya sunucusu ise CPU'yu tüketir. Medya dosyalarını gerçek iş hacmine sahip bir disk biriminde tutun ve sunucuda başka kritik işler yapıyorsanız indirme istemcisi üzerinde bant genişliği sınırı belirleyin. Bu diğer işler için kapasiteyi hazır varsaymak yerine, bütçeyi ayrı ayrı planlayın: kendi kendine barındırılan bir AFFiNE çalışma alanı, arkasında bir veritabanı olan dört container daha demektir ve 2 GB'lık bir makinede bu belleğin büyük kısmını kendisine ister. Her ek servis bu kadar maliyetli değildir: kendi kendine barındırılan bir openGym antrenman takipçisi gibi tek amaçlı servisler, kendi TLS yapılandırmasını sağladığınız ve bir yıllık antrenman geçmişini emanet etmeden önce veritabanı dosyasının nerede olduğunu bildiğiniz sürece makineyi sorunsuz paylaşır. Bir web uygulaması, bir Postgres veritabanı ve bir arka plan iş kuyruğu barındıran her şey, bu ölçeğin AFFiNE tarafına daha yakındır; bu nedenle kendi kendine barındırılan bir Chatwoot destek masası uygulamasının bu sunucuda mı yoksa kendi sunucusunda mı yer alacağına, bir içe aktarma işleminin ortasında sınırla karşılaşmadan önce karar verin. Ani yük artışlarına sahip iş yükleri daha fazla dikkat gerektirir, çünkü bir içe aktarma işlemiyle çakışan şey ortalama değil, tepe değerleridir: Eğer her kişiye kendi korumalı aracısını sunan, kendi kendine barındırılan bir OneCLI kurulumunu düşünüyorsanız, yayınlanan boyutlandırma rakamlarını free -h üzerinde boşta duran bir makinede görünen değerlerle değil, qBittorrent tam kapasite çalışırken gerçekte boş olan kaynaklarla karşılaştırın.
FAQ
Sonarr neden dosyaları hardlink yapmak yerine kopyalıyor?
Çünkü kaynak ve hedef, container bakış açısına göre farklı dosya sistemleri üzerindedir. /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, indirmeleri ve kütüphaneyi bunun içine yerleştirin; böylece link oluşturma mümkün hale gelir. Sonucu her iki dosya üzerinde stat -c '%i %h %n' ile doğrulayın: aynı inode numarası 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 bilgiyi id -u ve id -g komutlarıyla alabilirsiniz. Yeni bir Ubuntu VPS üzerinde bu değerler genellikle her ikisi için de 1000'tir. 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. Her yayınlanan portu Compose dosyasında 127.0.0.1 adresine bağlayın, ardından arayüzlere bir SSH tüneli üzerinden veya sunucuda TLS (transport layer security) sonlandırması yapan ve kendi kimlik doğrulamasını ekleyen bir reverse proxy aracılığıyla erişin. Yalnızca bir VPN, 127.0.0.1 adresine bağlı bir porta erişemez çünkü Docker bunu yalnızca ana makinenin kendisinden erişilebilir kılar. VPN üzerinden doğrudan erişim istiyorsanız, portları sunucunun VPN adresi üzerinde yayınlayın. Portları doğrudan genel arayüzde 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 durduramaz.
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 menüsü 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; böylece kütüphaneler /media/Movies ve /media/Shows konumunda yer alırken, Sonarr ve Radarr aynı dizinlere /data/media üzerinden yazmaya devam eder. Medya sunucusuna aynı PUID ve PGID değerlerini verin, böylece arr stack'inin yazdığı dosyaları okuyabilir.