SSD Nodes Learn 8GB RAM — $66/سنة
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-01

ملف Docker Compose واحد لحزمة arr كاملة

شغّل Prowlarr وSonarr وRadarr وqBittorrent على VPS بملف Compose واحد، مع PUID وPGID وتخطيط مجلدات مشترك يحافظ على عمل الروابط الصلبة.

ما الذي ستبنيه

تتكون حزمة arr مع Docker Compose من 4 حاويات تدير مكتبة وسائط: يتولى Prowlarr إعدادات المفهرسات، ويتولى Sonarr المسلسلات، ويتولى Radarr الأفلام، ويتولى qBittorrent عميل التنزيل. تتواصل هذه الحاويات عبر شبكة Compose باستخدام أسماء الخدمات، وتشترك في شجرة مجلدات واحدة على المضيف. التثبيت سريع. لكن تخطيط وحدات التخزين هو العامل الذي يحدد ما إذا كانت الحزمة ستعمل لسنوات أو ستسبب لك مشكلات كل أسبوع، لذلك يركز معظم هذا الدليل عليه.

لا تبحث الحزمة عن المحتوى نيابةً عنك. يحتفظ Prowlarr بالمفهرسات التي تضيفها إليه، واستخدامك لهذه المفهرسات قرارك ومسؤوليتك القانونية. يغطي هذا الدليل إعدادات الربط: المستخدمين، والمسارات، والأذونات، وشبكات الحاويات، وعمليات التحقق التي تثبت أن الإعداد يعمل.

إذا لم تكتب ملف Compose من قبل، فاقرأ أساسيات Docker Compose لخادم VPS أولًا. يفترض هذا المنشور أن docker compose version يطبع شيئًا على خادمك بالفعل.

لماذا تتعطل الروابط الصلبة، ولماذا هذا هو جوهر المشكلة

عندما ينتهي Sonarr من تنزيل ما، يستورد الملف إلى مكتبتك. إذا كان مجلد التنزيل ومجلد المكتبة موجودين على نظام الملفات نفسه، يكون الاستيراد رابطًا صلبًا: اسمًا ثانيًا يشير إلى البيانات نفسها على القرص. لا يستهلك ذلك مساحة إضافية ولا يستغرق وقتًا. يواصل التورنت البذر من الاسم القديم، بينما يقرأ خادم الوسائط الاسم الجديد.

إذا كان المجلدان على نظامي ملفات مختلفين، فلا يستطيع kernel إنشاء هذا الرابط. عندها يعود Sonarr إلى النسخ. سيستهلك موسم بحجم 40 GB الآن 80 GB من مساحة القرص وعدة دقائق من عمليات الإدخال والإخراج، وسيسجل سجل الاستيراد فشل إنشاء الرابط الصلب ونسخ الملف بدلًا منه. على VPS ذي حصة قرص ثابتة، بهذه الطريقة تنفد المساحة خلال أسبوع.

إليك موضع المشكلة. داخل حاوية، يكون bind mount حدًا بين نظامي ملفات. عند تركيب /mnt/data/torrents كـ /downloads و/mnt/data/media كـ /tv، يرى Sonarr تركيبين منفصلين، رغم وجودهما على قرص مضيف واحد، ويرفض إنشاء رابط بينهما. توضح وثائق صورة LinuxServer.io الرسمية ذلك مباشرة: استخدام المسارين المنفصلين /downloads و/tv يلغي إمكانية إنشاء روابط صلبة.

الحل هو استخدام تركيب واحد. تحصل كل حاوية تتعامل مع الوسائط على وحدة التخزين نفسها، /mnt/data:/data، ويكون كل مسار تستخدمه مجلدًا داخلها. نقطة تركيب واحدة، ونظام ملفات واحد، وروابط صلبة تعمل.

إنشاء المستخدم والمجموعة والمجلدات

تكتب الحاويات الملفات باستخدام معرّف مستخدم رقمي، وتحدده PUID وPGID. استخدم حسابك الخاص حتى تتمكن من قراءة هذه الملفات وتحريرها عبر SSH من دون sudo.

id -u
id -g

يعرض كلا الأمرين عادةً 1000 على Ubuntu VPS جديد. أنشئ الآن البنية الشجرية. ضعها على القرص الذي يحتوي على الوسائط، وحافظ على وجود البنية كاملة على هذا القرص الواحد.

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

تحقق من أنها تستخدم نظام ملفات واحدًا فعلًا قبل المتابعة:

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

يجب أن يعرض السطران جهاز المصدر نفسه. يعني اختلاف الجهازين أن الروابط الصلبة لن تعمل مطلقًا، مهما كانت إعداداتك في تهيئة الحاوية.

سُمّيت مجلدات المكتبة Movies وShows عن قصد. إذا كنت تشغّل Jellyfin كخادم للوسائط بالفعل، فقم بتركيب /mnt/data/media في Jellyfin باعتباره /media، وستصل مكتبته إلى /media/Movies و/media/Shows، تمامًا كما يحددها ذلك الدليل.

ملف البيئة

احتفظ بالقيم التي تختلف من خادم إلى آخر في .env، بجوار ملف Compose.

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 متطابقة في الحاويات الثلاث التي تصل إلى الوسائط. ولا يحصل Prowlarr عليها، لأنه لا يفتح ملفات الوسائط مطلقًا.

يرتبط كل منفذ ويب بـ 127.0.0.1، لذلك ينشره Docker على عنوان loopback فقط. أما 8989:8989 العادي، فسوف ينشره على كل الواجهات، وستمرر قواعد الجدار الناري الخاصة بـ Docker حركة الشبكة هذه مباشرةً متجاوزةً قاعدة deny في ufw. يفاجئ هذا السلوك المستخدمين باستمرار، وهو مشروح في سبب نشر Docker للمنافذ مباشرةً عبر ufw.

يُنشر المنفذ 6881 على جميع الواجهات عمدًا. هذا هو منفذ الاستماع الخاص بالتورنت، ويجب أن يكون قابلًا للوصول لاتصالات النظراء الواردة. اسمح به باستخدام sudo ufw allow 6881، واقرأ أساسيات جدار ufw الناري لخادم VPS إذا كان هذا الأمر جديدًا عليك.

تكون أدلة الإعداد منفصلة لكل تطبيق، ولا تتم مشاركة سوى وحدة تخزين الوسائط. أنشئها قبل التشغيل الأول حتى تكون مملوكةً للمستخدم الخاص بك بدلًا من root:

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

يجب أن تقرأ الخدمات الأربع running. اعتبارًا من July 2026، تُنشر هذه الصور على lscr.io، وتتبع العلامة latest الإصدار المستقر الحالي. لذلك ثبّت علامة إصدار بدلًا منها إذا أردت أن تكون الترقيات قرارًا تتخذه، لا مفاجأة.

الوصول إلى واجهات الويب بأمان

بما أن المنافذ تستمع على loopback، فلا شيء مكشوف حتى الآن. أعد توجيهها عبر SSH من جهازك:

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

يمكنك الآن الوصول إلى Sonarr على الخادم من خلال http://127.0.0.1:8989 في متصفحك. للوصول الدائم، ضع المكدس خلف Traefik مع شهادات TLS لعدة تطبيقات، أو صِل إلى الخادم عبر شبكة WireGuard VPN تستضيفها بنفسك. يجب ألا يكون أي من هذه التطبيقات على الإنترنت العام مع صفحة تسجيل الدخول الخاصة به فقط كطبقة حماية.

ينشئ qBittorrent كلمة مرور عشوائية للمسؤول عند التشغيل الأول، ويطبعها في سجل الحاوية. اقرأها، ثم غيّرها في واجهة الويب:

docker compose logs qbittorrent | grep -i password

إذا تخطيت تغييرها، فسيُنشئ التطبيق كلمة مرور عشوائية جديدة عند كل إعادة تشغيل، وستضطر إلى الرجوع إلى السجلات في كل مرة.

عيّن المسارات داخل كل تطبيق

في qBittorrent، افتح Options، ثم Downloads، وعيّن مسار الحفظ الافتراضي إلى /data/torrents. أبقِ مجلد التنزيلات غير المكتملة داخل الشجرة نفسها، مثل /data/torrents/incomplete. لا يمكن إنشاء رابط صلب لتنزيل يكتمل في أي مكان خارج /data داخل المكتبة.

في Sonarr، افتح Settings، ثم Media Management، وأضف المجلد الجذر /data/media/Shows. أما في Radarr، فالمجلد الجذر هو /data/media/Movies. هذه مسارات داخل الحاوية. يُرفض مسار المضيف /mnt/data/media/Shows لأن هذا الدليل غير موجود من منظور الحاوية.

في كل من Sonarr وRadarr، افتح Settings، ثم Download Clients، وأضف qBittorrent. المضيف هو qbittorrent والمنفذ هو 8080. يعمل اسم الخدمة كاسم مضيف لأن Compose يضع الحاويات الأربع على شبكة واحدة مع خدمة DNS داخلية (نظام أسماء النطاقات). لا تستخدم localhost هنا: داخل حاوية Sonarr، يكون localhost هو Sonarr.

اترك Remote Path Mappings فارغة. تُستخدم هذه الميزة لتحويل مسار يبلّغ عنه عميل التنزيل إلى مسار يمكن لتطبيق arr رؤيته. وبوجود عملية تركيب مشتركة واحدة لـ /data، يتفق كلا التطبيقين بالفعل على كل مسار، وهذا هو السبب الثاني الذي يجعل هذا التخطيط يستحق الجهد.

ربط Prowlarr بـ Sonarr وRadarr

يدفع Prowlarr تعريفات مفهرسات المحتوى إلى التطبيقات الأخرى، لذلك تضبط المفهرس مرة واحدة بدلًا من مرتين. ويحتاج إلى مفتاح API (واجهة برمجة التطبيقات) من كل تطبيق.

في Sonarr، افتح Settings، ثم General، وانسخ مفتاح API. في Prowlarr، افتح Settings، ثم Apps، وأضف تطبيق Sonarr، ثم املأ 3 حقول. قيمة Prowlarr Server هي http://prowlarr:9696. قيمة Sonarr Server هي http://sonarr:8989. قيمة API Key هي القيمة التي نسختها. اضغط Test. تعني النتيجة الخضراء أن Prowlarr وصل إلى Sonarr عبر شبكة Compose. كرر الخطوات مع Radarr على http://radarr:7878.

تعني النتيجة الحمراء التي تفيد برفض الاتصال في معظم الحالات أن اسم الخدمة غير صحيح أو أن البادئة http:// مفقودة. تأكد من إمكانية تحليل الاسم من داخل الحاوية:

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

يثبت رمز حالة HTTP أن مسار الشبكة يعمل. ويثبت خطأ تحليل الاسم أن اسم الخدمة غير صحيح.

تحقق من إنشاء الروابط الصلبة فعليًا

لا تعتمد على الإعداد قبل التحقق من عدد الروابط. بعد استيراد عنصر واحد، قارن الملف الذي تم تنزيله بملف المكتبة:

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

الرقم الأول هو inode، والرقم الثاني هو عدد الروابط. يظهر الملف الذي أُنشئ له رابط صلب بنفس inode في كلا الموقعين، ويكون عدد الروابط 2. يشير اختلاف inode في الموقعين، مع وجود عدد روابط 1 لكل منهما، إلى أن Sonarr نسخ الملف. وسيذكر سجل الاستيراد أن إنشاء الرابط الصلب فشل.

راقب مساحة القرص أيضًا. يجب أن يتغير df -h /mnt/data تغيرًا طفيفًا جدًا عند إجراء الاستيراد، لأن الرابط الصلب يضيف اسمًا ولا يضيف بيانات.

ما الذي يتعطل فعليًا

تعني أخطاء الأذونات عند الاستيراد أن معرّف مستخدم الحاوية لا يستطيع الكتابة في مجلد المكتبة. الرسالة هي Access to the path ... is denied. تحقّق باستخدام ls -ln /mnt/data/media من أن معرّف المالك يطابق PUID، وتذكّر أن المجلدات تحتاج إلى بت التنفيذ قبل أن تتمكّن الحاوية من الدخول إليها.

تعني الملفات التي تظهر مملوكةً لـ root أن الحاوية بدأت قبل وجود مجلد المضيف، ولذلك أنشأه Docker بصفة root. أوقف المكدس، chown المجلد، ثم شغّله مرة أخرى.

إذا حذفت تورنتًا من qBittorrent ووجدت أن ملف المكتبة اختفى، فهذا يعني أن الاستيراد كان عملية نسخ حُذف ناتجها لاحقًا، أو أنك حذفت البيانات بدلًا من إدخال التورنت. عند استخدام hardlink حقيقي، يؤدي حذف أحد الاسمين إلى إبقاء الآخر سليمًا، لأن البيانات لا تُحرَّر إلا عندما يصل عدد الروابط إلى الصفر.

امتلاء القرص بسرعة أكبر من حجم الوسائط التي أضفتها هو أكثر أشكال مشكلة النسخ تكلفةً. شغّل فحص stat أعلاه قبل شراء مساحة تخزين إضافية.

ما يحتاجه هذا المكدس من خادم VPS

تطبيقات arr الثلاثة خفيفة. فهي تستطلع المفهرسات، وتكتب في قاعدة بيانات SQLite صغيرة، وتعيد تسمية الملفات. يشغّل خادم بذاكرة RAM سعتها 2 GB الحاويات الأربع كلها بسهولة. يأتي الحمل من مكان آخر. يستنفد عميل التنزيل إدخال القرص وإخراجه عند التعامل مع ملفات torrent كبيرة، كما سيستهلك خادم وسائط يرمّز الفيديو على الخادم نفسه قدرة CPU. احتفظ بالوسائط على وحدة تخزين ذات معدل نقل فعلي، واضبط حدًا لعرض النطاق الترددي في عميل التنزيل إذا كان الخادم ينفّذ أي مهام أخرى مهمة لك.

FAQ

لماذا ينسخ Sonarr الملفات بدلًا من إنشاء روابط صلبة؟

لأن المصدر والوجهة موجودان في نظامي ملفات مختلفين من منظور الحاوية. يُعد كل من نقطتي الربط المنفصلتين، مثل /downloads و/tv، نظام ملفات مستقلًا حتى عندما تأتيان من قرص مضيف واحد. اربط دليلًا أصلًا واحدًا باسم /data في كل حاوية، وضع التنزيلات والمكتبة داخله، وعندها يصبح إنشاء الرابط ممكنًا. أكّد النتيجة باستخدام stat -c '%i %h %n' على كلا الملفين: يجب أن تكون قيمة inode متطابقة، وأن يكون عدد الروابط 2.

ما PUID وPGID اللذان ينبغي أن أستخدمهما؟

استخدم المعرّف الرقمي لحساب المضيف الذي يملك شجرة الوسائط، ويمكنك الحصول عليه باستخدام id -u وid -g. في Ubuntu VPS جديد، تكون القيمة عادةً 1000 لكليهما. يجب أن تستخدم كل حاوية في المكدس الزوج نفسه، وإلا فسيكتب أحد التطبيقات ملفات لا يستطيع تطبيق آخر تعديلها. بعد تغيير القيم، أعد إنشاء الحاويات باستخدام docker compose up -d --force-recreate، وأصلح الملفات الموجودة باستخدام chown -R.

هل أحتاج إلى إتاحة واجهات الويب هذه عبر الإنترنت؟

لا، ولا ينبغي لك ذلك. اربط كل منفذ منشور بالعنوان 127.0.0.1 في ملف Compose، ثم صِل إلى الواجهات عبر نفق SSH أو VPN أو وكيل عكسي ينهي TLS (أمان طبقة النقل) ويضيف آلية المصادقة الخاصة به. إن نشر هذه الواجهات مباشرة أسوأ مما يبدو، لأن Docker يضيف قواعد جدار حماية خاصة به، ولن تمنع قاعدة ufw باسم deny حركة المرور هذه.

أين أجد كلمة مرور qBittorrent؟

تعرض صورة LinuxServer.io كلمة مرور مؤقتة للمستخدم admin في سجل بدء التشغيل. شغّل docker compose logs qbittorrent | grep -i password لقراءتها، ثم عيّن كلمة مرور دائمة من Options وWeb UI. تُنشأ كلمة مرور مؤقتة جديدة عند كل إعادة تشغيل إلى أن تعيّن كلمة مرورك الخاصة.

هل يمكن لـ Jellyfin استخدام المجلدات نفسها؟

نعم، وهذا هو الهدف من هذا التنظيم. اربط /mnt/data/media بخادم الوسائط لديك باسم /media، وستوجد مكتبته في /media/Movies و/media/Shows، بينما يكتب Sonarr وRadarr في الدليلين نفسيهما عبر /data/media. امنح خادم الوسائط قيمتي PUID وPGID نفسيهما حتى يتمكن من قراءة ما يكتبه مكدس arr.