SSD Nodes Learn 8GB RAM — $66/سال
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-01

ایک Docker Compose فائل میں Prowlarr، Sonarr، Radarr stack

VPS پر Prowlarr، Sonarr، Radarr اور qBittorrent ایک Docker Compose فائل سے چلائیں۔ مشترکہ PUID، PGID اور volume layout hardlinks کو درست رکھتے ہیں۔

آپ کیا بنا رہے ہیں

Docker Compose کا arr stack چار containers پر مشتمل ہوتا ہے جو media library کا انتظام کرتے ہیں: indexer settings کے لیے Prowlarr، series کے لیے Sonarr، films کے لیے Radarr، اور download client کے طور پر qBittorrent۔ یہ Compose network پر service name کے ذریعے ایک دوسرے سے رابطہ کرتے ہیں، اور host پر ایک ہی folder tree شیئر کرتے ہیں۔ انسٹالیشن مختصر ہے۔ وہ چیز جو طے کرتی ہے کہ stack برسوں تک درست کام کرے گا یا ہر ہفتے مسئلہ پیدا کرے گا، volume layout ہے۔ اسی لیے اس guide کا زیادہ تر حصہ اسی موضوع پر ہے۔

یہ stack آپ کے لیے content تلاش نہیں کرتا۔ Prowlarr میں وہی indexers شامل ہوتے ہیں جو آپ اس میں add کرتے ہیں، اور کون سے indexers استعمال کرنے ہیں، یہ آپ کا فیصلہ اور قانونی ذمہ داری ہے۔ یہ guide بنیادی انتظامی امور کا احاطہ کرتی ہے: users، paths، permissions، container networking، اور وہ checks جو ثابت کرتے ہیں کہ سب کچھ درست کام کر رہا ہے۔

اگر آپ نے کبھی Compose file نہیں لکھی، تو پہلے VPS کے لیے Docker Compose کی بنیادی باتیں پڑھیں۔ یہ post فرض کرتی ہے کہ docker compose version آپ کے server پر پہلے ہی کچھ output دکھاتا ہے۔

ہارڈ لنکس کیوں ناکام ہوتے ہیں، اور اصل مسئلہ یہی کیوں ہے

جب Sonarr ڈاؤن لوڈ مکمل کرتا ہے تو یہ فائل کو آپ کی لائبریری میں درآمد کرتا ہے۔ اگر ڈاؤن لوڈ فولڈر اور لائبریری فولڈر ایک ہی filesystem پر ہوں تو درآمد hardlink ہوتی ہے: یہ ڈسک پر موجود اسی ڈیٹا کی طرف اشارہ کرنے والا دوسرا نام ہوتا ہے۔ اس کے لیے اضافی جگہ یا وقت درکار نہیں ہوتا۔ torrent پرانے نام سے seeding جاری رکھتا ہے، جبکہ media server نئے نام سے فائل پڑھتا ہے۔

اگر دونوں فولڈر مختلف filesystems پر ہوں تو kernel یہ link نہیں بنا سکتا۔ Sonarr copy پر واپس چلا جاتا ہے۔ اب 40 GB کا سیزن ڈسک پر 80 GB جگہ اور input/output کے کئی منٹ لیتا ہے، اور import log درج کرتا ہے کہ hardlink ناکام ہوئی اور اس کے بجائے فائل copy کی گئی۔ مقررہ disk allowance والے VPS پر لوگ اسی طرح ایک ہفتے میں جگہ ختم کر بیٹھتے ہیں۔

اصل مسئلہ یہ ہے۔ container کے اندر bind mount ایک filesystem boundary ہوتا ہے۔ /mnt/data/torrents کو /downloads کے طور پر اور /mnt/data/media کو /tv کے طور پر mount کریں تو دونوں ایک ہی host disk پر ہونے کے باوجود Sonarr انہیں دو الگ mounts سمجھتا ہے اور ان کے درمیان link بنانے سے انکار کرتا ہے۔ LinuxServer.io کی سرکاری image documentation میں یہ بات براہ راست لکھی ہے: الگ /downloads اور /tv paths استعمال کرنے سے hardlink بنانے کی صلاحیت ختم ہو جاتی ہے۔

حل ایک mount ہے۔ media استعمال کرنے والے ہر container کو ایک ہی volume، /mnt/data:/data، ملتا ہے، اور ان کے استعمال کردہ ہر path اسی کے اندر موجود ایک folder ہوتا ہے۔ ایک mount point، ایک filesystem، اور کام کرنے والی hardlinks۔

صارف، گروپ اور فولڈرز بنائیں

کنٹینرز فائلیں عددی user ID کے ساتھ لکھتے ہیں، جسے PUID اور PGID متعین کرتے ہیں۔ اپنا account استعمال کریں تاکہ آپ sudo کے بغیر SSH کے ذریعے ان فائلوں کو پڑھ اور ترمیم کر سکیں۔

id -u
id -g

نئے Ubuntu VPS پر دونوں عموماً 1000 پرنٹ کرتے ہیں۔ اب directory tree بنائیں۔ اسے اس disk پر رکھیں جس پر آپ کا media موجود ہے، اور پوری tree اسی ایک disk پر رکھیں۔

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

آگے بڑھنے سے پہلے تصدیق کریں کہ یہ واقعی ایک ہی filesystem ہے:

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

دونوں سطروں میں ایک ہی source device دکھائی دینا چاہیے۔ دو مختلف devices کا مطلب ہے کہ hardlinks کبھی کام نہیں کریں گے، چاہے آپ container config میں کچھ بھی set کریں۔

library folders کے نام جان بوجھ کر Movies اور Shows رکھے گئے ہیں۔ اگر آپ پہلے ہی Jellyfin کو اپنے media server کے طور پر چلا رہے ہیں، تو /mnt/data/media کو Jellyfin میں /media کے طور پر mount کریں۔ اس طرح اس کی libraries /media/Movies اور /media/Shows میں آ جائیں گی، بالکل اسی جگہ جہاں وہ guide انہیں رکھتی ہے۔

ماحولیاتی فائل

ہر سرور کے لحاظ سے تبدیل ہونے والی قدریں Compose فائل کے ساتھ موجود .env میں رکھیں۔

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

~/arr/.env لکھیں:

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

TZ کو اپنے علاقے پر سیٹ کریں، جیسے Europe/Berlin۔ arr ایپلیکیشنز اسی علاقے میں کاموں کا شیڈول بناتی ہیں اور لاگ کی سطروں پر وقت درج کرتی ہیں، اس لیے غلط قدر بعد میں تمام لاگز کو مبہم بنا دے گی۔

Compose فائل

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

اس فائل میں چار چیزیں اہم کام کر رہی ہیں۔

${DATA_ROOT}:/data ان تینوں containers میں یکساں ہے جو media کو استعمال کرتے ہیں۔ Prowlarr کو یہ volume نہیں ملتا، کیونکہ Prowlarr کبھی media فائل نہیں کھولتا۔

ہر web port کو 127.0.0.1 کے ساتھ bind کیا گیا ہے، اس لیے Docker اسے صرف loopback address پر publish کرتا ہے۔ ایک سادہ 8989:8989 اسے ہر interface پر publish کر دے گا، اور Docker کے اپنے firewall rules اس traffic کو ufw کے deny rule سے براہِ راست آگے گزار دیں گے۔ یہ رویہ اکثر لوگوں کو حیران کرتا ہے۔ اس کی وضاحت Docker کے ports کو ufw کے ذریعے براہِ راست publish کرنے کی وجہ میں ہے۔

Port 6881 کو جان بوجھ کر تمام interfaces پر publish کیا گیا ہے۔ یہ torrent listening port ہے، اور incoming peer connections کے لیے اس تک رسائی ضروری ہے۔ اسے sudo ufw allow 6881 کے ذریعے allow کریں۔ اگر یہ command نئی ہے تو VPS کے لیے ufw firewall کی بنیادی باتیں پڑھیں۔

ہر application کے لیے config directories الگ ہیں، اور صرف media volume مشترک ہے۔ پہلی بار start کرنے سے پہلے انہیں بنائیں تاکہ ان کی ملکیت root کے بجائے آپ کے user کے پاس ہو:

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

چاروں services کو running پڑھنا چاہیے۔ July 2026 تک یہ images lscr.io پر publish کی جاتی ہیں، اور latest tag موجودہ stable release کی پیروی کرتا ہے۔ اگر آپ upgrades کو اچانک ہونے کے بجائے باقاعدہ فیصلہ بنانا چاہتے ہیں تو اس کے بجائے version tag pin کریں۔

ویب انٹرفیس تک محفوظ طریقے سے رسائی حاصل کریں

چونکہ یہ ports loopback پر ہیں، اس لیے ابھی کچھ بھی بیرونی طور پر exposed نہیں ہے۔ اپنی مشین سے انہیں SSH کے ذریعے forward کریں:

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

اب اپنے browser میں http://127.0.0.1:8989 سرور پر Sonarr تک رسائی فراہم کرتا ہے۔ مستقل رسائی کے لیے stack کو متعدد apps کے لیے TLS certificates کے ساتھ Traefik کے پیچھے رکھیں، یا سرور تک اپنے زیرِ انتظام WireGuard VPN کے ذریعے رسائی حاصل کریں۔ ان میں سے کسی بھی application کو صرف اس کے اپنے login page کے ساتھ public internet پر نہیں رکھنا چاہیے۔

qBittorrent پہلی بار شروع ہونے پر ایک random administrator password بناتا ہے اور اسے container log میں لکھتا ہے۔ اسے پڑھیں، پھر web interface میں تبدیل کریں:

docker compose logs qbittorrent | grep -i password

اگر آپ password تبدیل نہیں کرتے، تو ہر restart پر نیا random password بنایا جائے گا، اور آپ کو ہر بار logs دیکھنے پڑیں گے۔

ہر application کے اندر paths مقرر کریں

qBittorrent میں Options کھولیں، پھر Downloads پر جائیں، اور default save path کو /data/torrents مقرر کریں۔ incomplete-downloads folder کو اسی tree کے اندر رکھیں، مثلاً /data/torrents/incomplete۔ /data کے باہر کہیں مکمل ہونے والا download library میں hardlink نہیں کیا جا سکتا۔

Sonarr میں Settings کھولیں، پھر Media Management پر جائیں، اور root folder /data/media/Shows شامل کریں۔ Radarr میں root folder /data/media/Movies ہے۔ یہ container کے اندر موجود paths ہیں۔ host path /mnt/data/media/Shows مسترد ہو جاتا ہے، کیونکہ container کے نقطۂ نظر سے وہ directory موجود نہیں ہے۔

Sonarr اور Radarr دونوں میں Settings کھولیں، پھر Download Clients پر جائیں، اور qBittorrent شامل کریں۔ host qbittorrent ہے اور port 8080 ہے۔ service name hostname کے طور پر کام کرتا ہے، کیونکہ Compose تمام چار containers کو internal DNS (domain name system) service والے ایک ہی network پر رکھتا ہے۔ یہاں localhost استعمال نہ کریں: Sonarr container کے اندر localhost، Sonarr ہے۔

Remote Path Mappings کو خالی چھوڑیں۔ یہ feature اس path کو تبدیل کرنے کے لیے ہے جس کی اطلاع download client دیتا ہے، تاکہ وہ path arr application کو دکھائی دے سکے۔ ایک مشترک /data mount کے ساتھ دونوں containers پہلے ہی ہر path پر متفق ہیں۔ یہی دوسری وجہ ہے کہ یہ layout اضافی کام کے قابل ہے۔

Prowlarr کو Sonarr اور Radarr سے مربوط کریں

Prowlarr انڈیکسر کی تعریفیں دیگر ایپلیکیشنز میں منتقل کرتا ہے، اس لیے آپ کو انڈیکسر صرف ایک بار ترتیب دینا پڑتا ہے، دو بار نہیں۔ اس کے لیے ہر ایپلیکیشن کی API (application programming interface) key درکار ہے۔

Sonarr میں Settings کھولیں، پھر General کھول کر API key نقل کریں۔ Prowlarr میں Settings کھولیں، پھر Apps کھولیں، Sonarr ایپلیکیشن شامل کریں اور تین فیلڈز پُر کریں۔ Prowlarr Server میں http://prowlarr:9696 درج کریں۔ Sonarr Server میں http://sonarr:8989 درج کریں۔ API Key میں نقل کی گئی قدر درج کریں۔ Test دبائیں۔ سبز نتیجہ ظاہر کرتا ہے کہ Prowlarr نے Compose نیٹ ورک کے ذریعے Sonarr تک رسائی حاصل کر لی ہے۔ Radarr کے لیے http://radarr:7878 پر یہی عمل دہرائیں۔

کنکشن مسترد ہونے کا سرخ نتیجہ تقریباً ہمیشہ غلط سروس نام یا http:// prefix کے غائب ہونے کی نشاندہی کرتا ہے۔ تصدیق کریں کہ container کے اندر سے نام resolve ہو رہا ہے:

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

HTTP status code ثابت کرتا ہے کہ نیٹ ورک path درست ہے۔ Name resolution error ثابت کرتا ہے کہ سروس نام غلط ہے۔

جب تک آپ link count دیکھ نہ لیں، setup پر اعتماد نہ کریں۔ ایک item کے import ہونے کے بعد downloaded file اور library file کا موازنہ کریں:

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

پہلا نمبر inode اور دوسرا link count ہے۔ hardlinked file دونوں مقامات پر ایک ہی inode اور 2 کا link count دکھاتی ہے۔ اگر دو مختلف inodes ہوں اور ہر ایک کا link count 1 ہو، تو اس کا مطلب ہے کہ Sonarr نے file copy کی ہے۔ import log میں hardlink ناکام ہونے کا پیغام بھی موجود ہوگا۔

disk کو بھی monitor کریں۔ import کے وقت df -h /mnt/data میں بمشکل تبدیلی آنی چاہیے، کیونکہ hardlink صرف ایک نام شامل کرتا ہے اور کوئی data شامل نہیں کرتا۔

اصل میں کیا خراب ہوتا ہے

درآمد کے دوران اجازت کی خرابیوں کا مطلب ہے کہ container کا user id library folder میں لکھ نہیں سکتا۔ پیغام Access to the path ... is denied ہے۔ ls -ln /mnt/data/media سے جانچیں کہ owner id آپ کے PUID سے مطابقت رکھتی ہے، اور یاد رکھیں کہ container کے ان میں داخل ہونے سے پہلے directories پر execute bit درکار ہوتا ہے۔

root کی ملکیت والی نظر آنے والی files کا مطلب ہے کہ container اس وقت شروع ہوا جب host directory موجود نہیں تھی، اس لیے Docker نے اسے root کے طور پر بنادیا۔ stack روکیں، directory کو chown کریں، پھر اسے دوبارہ شروع کریں۔

qBittorrent سے torrent حذف کرنے کے بعد library file کے غائب ہونے کا مطلب ہے کہ import ایک copy تھا جسے بعد میں حذف کردیا گیا، یا آپ نے torrent entry کے بجائے data حذف کردیا۔ حقیقی hardlink کے ساتھ ایک نام حذف کرنے سے دوسرا نام برقرار رہتا ہے، کیونکہ data صرف اس وقت آزاد ہوتا ہے جب link count صفر تک پہنچ جائے۔

اگر disk آپ کے شامل کردہ media سے زیادہ تیزی سے بھر رہی ہے تو یہ copy کے مسئلے کی سب سے مہنگی صورت ہے۔ مزید storage خریدنے سے پہلے اوپر دیا گیا stat check چلائیں۔

اس stack کو VPS سے کیا درکار ہے

تینوں arr applications کم وسائل استعمال کرتی ہیں۔ یہ indexers سے وقفے وقفے سے ڈیٹا حاصل کرتی ہیں، ایک چھوٹے SQLite database میں لکھتی ہیں، اور فائلوں کے نام تبدیل کرتی ہیں۔ 2 GB RAM والا server چاروں containers کو باآسانی چلا سکتا ہے۔ اصل بوجھ دوسری جگہ سے آتا ہے۔ بڑے torrents کے دوران download client disk کے input اور output کو مکمل طور پر مصروف کر دیتا ہے، اور اسی box پر video کو transcode کرنے والا media server CPU استعمال کرے گا۔ media کو مناسب throughput والے volume پر رکھیں، اور اگر server پر کوئی دوسرا اہم کام بھی ہو رہا ہو تو download client پر bandwidth limit مقرر کریں۔

FAQ

اس کی وجہ یہ ہے کہ container کے نقطۂ نظر سے source اور destination مختلف filesystems پر ہیں۔ دو الگ bind mounts، جیسے /downloads اور /tv، دو filesystems ہوتے ہیں، چاہے دونوں ایک ہی host disk سے آئے ہوں۔ ہر container میں ایک ہی parent directory کو /data کے طور پر mount کریں، اور اس کے اندر downloads اور library رکھیں۔ اس طرح link بنانا ممکن ہو جاتا ہے۔ دونوں files پر stat -c '%i %h %n' چلا کر نتیجہ تصدیق کریں: inode ایک جیسا ہونا چاہیے اور link count 2 ہونا چاہیے۔

مجھے کون سا PUID اور PGID استعمال کرنا چاہیے؟

Media tree کے مالک host account کی numeric id استعمال کریں۔ یہ id آپ id -u اور id -g سے حاصل کر سکتے ہیں۔ نئے Ubuntu VPS پر عموماً دونوں کے لیے 1000 ہوتا ہے۔ Stack کے ہر container میں یہی pair استعمال ہونا چاہیے، ورنہ ایک application ایسی فائلیں لکھے گی جن میں دوسری ترمیم نہیں کر سکے گی۔ اقدار تبدیل کرنے کے بعد docker compose up -d --force-recreate سے containers دوبارہ بنائیں، اور موجودہ فائلوں کی ملکیت chown -R سے درست کریں۔

کیا مجھے ان web interfaces کو internet پر expose کرنا ضروری ہے؟

نہیں، اور آپ کو ایسا نہیں کرنا چاہیے۔ Compose file میں ہر published port کو 127.0.0.1 سے bind کریں۔ اس کے بعد interfaces تک SSH tunnel، VPN، یا ایسے reverse proxy کے ذریعے رسائی حاصل کریں جو TLS (transport layer security) کو terminate کرے اور اپنی authentication شامل کرے۔ انہیں براہ راست publish کرنا بظاہر سے بھی زیادہ خطرناک ہے، کیونکہ Docker اپنے firewall rules شامل کرتا ہے، اور ufw کا deny rule اس traffic کو نہیں روک سکے گا۔

qBittorrent کا password کہاں ملے گا؟

LinuxServer.io image، admin user کے لیے startup log میں ایک عارضی password لکھتی ہے۔ اسے پڑھنے کے لیے docker compose logs qbittorrent | grep -i password چلائیں، پھر Options اور Web UI کے تحت مستقل password مقرر کریں۔ جب تک آپ اپنا password مقرر نہیں کرتے، ہر restart پر نیا عارضی password generate ہوتا ہے۔

کیا Jellyfin وہی folders استعمال کر سکتا ہے؟

ہاں، اور layout کا مقصد بھی یہی ہے۔ /mnt/data/media کو اپنے media server میں /media کے طور پر mount کریں۔ اس کی libraries /media/Movies اور /media/Shows پر موجود ہوں گی، جبکہ Sonarr اور Radarr انہی directories میں /data/media کے ذریعے لکھیں گے۔ Media server کو وہی PUID اور PGID دیں تاکہ وہ ان فائلوں کو پڑھ سکے جو arr stack لکھتا ہے۔

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