SSD Nodes Learn Hosting plans →
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-08-28

הקמת מערך arr ב-Docker Compose: מדריך מלא

למדו להגדיר Prowlarr, Sonarr, Radarr ו-qBittorrent בקובץ Docker Compose אחד. המדריך מסביר כיצד להגדיר PUID, PGID ומבנה volumes תקין כדי להבטיח שפעולת hardlinks תעבוד ללא שגיאות.

מה אתם בונים

מערך ה-arr ב-Docker Compose מורכב מארבע מכולות המנהלות ספריית מדיה: Prowlarr להגדרות האינדקסרים, Sonarr לסדרות, Radarr לסרטים, ו-qBittorrent כלקוח הורדות. המכולות מתקשרות זו עם זו ברשת ה-Compose לפי שם השירות, והן חולקות עץ תיקיות אחד על המארח. ההתקנה עצמה קצרה. החלק שיקבע אם המערך יעבוד שנים ללא תקלות או יגרום לבעיות מדי שבוע הוא מבנה ה-volumes, ולכן רוב המדריך הזה מתמקד בכך.

המערך אינו מאתר תוכן עבורכם. Prowlarr מאחסן את האינדקסרים שתוסיפו לו, והבחירה באילו אינדקסרים להשתמש היא החלטה שלכם ואחריותכם המשפטית. מדריך זה מכסה את התשתית: משתמשים, נתיבים, הרשאות, תקשורת בין מכולות, והבדיקות שמוודאות שהכל עובד.

אם מעולם לא כתבתם קובץ Compose, קראו תחילה את יסודות Docker Compose עבור VPS. פוסט זה מניח ש-docker compose version כבר מציג פלט כלשהו בשרת שלכם.

כאשר Sonarr מסיים הורדה, הוא מייבא את הקובץ לספרייה שלכם. אם תיקיית ההורדות ותיקיית הספרייה נמצאות על אותה מערכת קבצים, הייבוא מתבצע כ-hardlink: שם שני המצביע על אותו מידע בדיסק. פעולה זו אינה צורכת שטח נוסף ואינה דורשת זמן. ה-torrent ממשיך לבצע seeding מהשם הישן, בעוד שרת המדיה שלכם קורא את הקובץ מהשם החדש.

אם שתי התיקיות נמצאות על מערכות קבצים שונות, ה-kernel אינו יכול ליצור את הקישור הזה. Sonarr חוזר לביצוע העתקה (copy). עונה של 40 GB תופסת כעת 80 GB בדיסק ודורשת דקות ארוכות של פעולות קלט ופלט, ויומן הייבוא מתעד שה-hardlink נכשל והקובץ הועתק במקום זאת. ב-VPS עם מכסת דיסק קבועה, כך אנשים נותרים ללא שטח אחסון תוך שבוע.

כאן טמון המלכוד. בתוך מכולה (container), ה-bind mount מהווה גבול של מערכת קבצים. אם תבצעו mount ל-/mnt/data/torrents כ-/downloads ול-/mnt/data/media כ-/tv, גם אם שניהם נמצאים על אותו דיסק מארח, Sonarr יראה שני mount points נפרדים ויסרב לבצע קישור ביניהם. התיעוד הרשמי של ה-image מבית LinuxServer.io מציין זאת במפורש: שימוש בנתיבים נפרדים כמו /downloads ו-/tv מקריב את היכולת להשתמש ב-hardlinks.

הפתרון הוא mount יחיד. כל מכולה שניגשת למדיה מקבלת את אותו volume יחיד, /mnt/data:/data, וכל נתיב שבו הם משתמשים הוא תיקייה בתוכו. נקודת עיגון אחת, מערכת קבצים אחת, ו-hardlinks תקינים.

יצירת המשתמש, הקבוצה והתיקיות

המכולות כותבות קבצים באמצעות מזהה משתמש מספרי (UID), שנקבע על ידי PUID ו-PGID. השתמשו בחשבון שלכם כדי שתוכלו לקרוא ולערוך את הקבצים הללו דרך SSH ללא צורך ב-sudo.

id -u
id -g

שניהם בדרך כלל מציגים את 1000 בשרת VPS חדש עם Ubuntu. כעת בנו את עץ התיקיות. מקמו אותו על הכונן שבו נמצאים קובצי המדיה שלכם, ושמרו את כל העץ על אותו כונן.

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

שתי השורות חייבות להציג את אותו התקן מקור. שני התקנים שונים משמעותם שקישורים קשיחים (hardlinks) לא יעבדו, ללא קשר להגדרות שתבחרו בתצורת המכולה.

תיקיות הספרייה נקראות Movies ו-Shows בכוונה תחילה. אם אתם כבר מריצים את Jellyfin כשרת המדיה שלכם, בצעו mount ל-/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 לא מקבל אותו, כיוון ש-Prowlarr לעולם אינו פותח קובץ מדיה.

כל פורט אינטרנט קשור ל-127.0.0.1, כך ש-Docker מפרסם אותו בכתובת ה-loopback בלבד. שימוש ב-8989:8989 פשוט יפרסם אותו בכל ממשק, וחוקי ה-firewall של Docker יעבירו את התעבורה הזו ישירות מעבר לחוק ufw deny. התנהגות זו מפתיעה משתמשים ללא הרף, והיא מוסברת ב-מדוע Docker מפרסם פורטים ישירות דרך ufw.

פורט 6881 מפורסם בכל הממשקים בכוונה תחילה. זהו פורט ההאזנה של ה-torrent, והוא חייב להיות נגיש עבור חיבורי peer נכנסים. אפשרו אותו באמצעות sudo ufw allow 6881, וקראו את יסודות ה-firewall מסוג 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. נכון ליולי 2026, אימג'ים אלו מפורסמים ב-lscr.io והתג latest עוקב אחר הגרסה היציבה הנוכחית, לכן קבעו תג גרסה ספציפי אם אתם מעדיפים ששדרוגים יהיו החלטה מודעת ולא הפתעה.

גישה מאובטחת לממשקי ה-web

מכיוון שהפורטים מוגדרים על ה-loopback, דבר אינו חשוף עדיין. בצעו להם Forwarding באמצעות 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

כעת, http://127.0.0.1:8989 בדפדפן שלכם יציג את Sonarr בשרת. לגישה קבועה, הציבו את ה-stack מאחורי Traefik עם תעודות TLS עבור מספר יישומים, או גשו לשרת באמצעות VPN מסוג WireGuard שאתם מארחים בעצמכם. אף אחד מהיישומים הללו לא אמור להיות חשוף לאינטרנט הציבורי כאשר רק דף ההתחברות שלו מגן עליו. אם בחרתם בנתיב של reverse proxy ואתם מעדיפים חשבון אחד לכל ארבעת הממשקים במקום לנהל ארבעה פרטי התחברות נפרדים, Authentik מאפשר לכם Single Sign-On בניהול עצמי ש-Traefik יכול לאכוף על כל בקשה באמצעות forward auth.

qBittorrent מייצר סיסמת מנהל אקראית בהפעלה הראשונה ומדפיס אותה ללוג של ה-container. קראו אותה, ולאחר מכן שנו אותה בממשק ה-web:

docker compose logs qbittorrent | grep -i password

אם תדלגו על שינוי הסיסמה, תיווצר סיסמה אקראית חדשה בכל אתחול, ותיאלצו לחזור ללוגים בכל פעם מחדש.

הגדרת הנתיבים בתוך כל יישום

ב-qBittorrent, פתחו את Options, עברו ל-Downloads, והגדירו את נתיב השמירה המוגדר כברירת מחדל ל-/data/torrents. שמרו את תיקיית ההורדות הלא-שלמות (incomplete-downloads) בתוך אותו עץ ספריות, למשל ב-/data/torrents/incomplete. הורדה שמסתיימת בכל מיקום מחוץ ל-/data לא תוכל לעבור קישור קשיח (hardlink) לספרייה.

ב-Sonarr, פתחו את Settings, עברו ל-Media Management, והוסיפו את תיקיית השורש /data/media/Shows. ב-Radarr תיקיית השורש היא /data/media/Movies. אלו הם נתיבים בתוך המכולה (container). הנתיב במארח (host) /mnt/data/media/Shows יידחה, כיוון שספרייה זו אינה קיימת מנקודת המבט של המכולה.

בשני היישומים, Sonarr ו-Radarr, פתחו את Settings, עברו ל-Download Clients, והוסיפו את qBittorrent. המארח הוא qbittorrent והפורט הוא 8080. שם השירות מתפקד כשם מארח (hostname) מכיוון ש-Compose מציב את כל ארבע המכולות על רשת אחת עם שירות DNS (מערכת שמות מתחם) פנימי. אל תשתמשו ב-localhost כאן: בתוך המכולה של Sonarr, ה-localhost הוא Sonarr עצמו.

השאירו את Remote Path Mappings ריק. תכונה זו קיימת כדי לתרגם נתיב שמדווח על ידי לקוח ההורדות לנתיב שהיישום מסדרת ה-arr יכול לראות. עם נקודת עיגון (mount) משותפת אחת ב-/data, שתי המכולות כבר מסונכרנות על כל נתיב, וזו הסיבה השנייה לכך שמבנה זה מצדיק את המאמץ.

חיבור Prowlarr ל-Sonarr ול-Radarr

Prowlarr דוחף הגדרות אינדקסרים ליישומים האחרים, כך שניתן להגדיר אינדקסר פעם אחת במקום פעמיים. לשם כך הוא זקוק ל-API key (מפתח ממשק תכנות יישומים) מכל אחד מהם.

ב-Sonarr, פתחו את Settings, לאחר מכן את General, והעתיקו את ה-API key. ב-Prowlarr, פתחו את Settings, לאחר מכן את Apps, הוסיפו יישום Sonarr, ומלאו שלושה שדות. ה-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 מוכיח שמסלול הרשת תקין. שגיאת תרגום שם (name resolution) מוכיחה ששם השירות שגוי.

אל תסמכו על ההגדרה עד שלא תראו את מונה הקישורים (link count). לאחר ייבוא פריט אחד, השוו בין הקובץ שהורד לבין הקובץ בספרייה:

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

המספר הראשון הוא ה-inode והשני הוא מונה הקישורים. קובץ שקושר ב-hardlink יציג את אותו ה-inode בשני המקומות ומונה קישורים של 2. שני inodes שונים, שכל אחד מהם בעל מונה קישורים של 1, משמעותם ש-Sonarr העתיק את הקובץ, ויומן הייבוא יציין כי ה-hardlink נכשל.

עקבו גם אחר הדיסק. df -h /mnt/data כמעט לא אמור להשתנות בעת ביצוע ייבוא, מכיוון ש-hardlink מוסיף שם בלבד ללא העתקת נתונים.

מה גורם לתקלות בפועל

שגיאות הרשאה בעת ייבוא מצביעות על כך שמזהה המשתמש (user id) של המכולה אינו מורשה לכתוב לתיקיית הספרייה. ההודעה היא Access to the path ... is denied. בדקו באמצעות ls -ln /mnt/data/media שהבעלים תואם ל-PUID שלכם, וזכרו שתיקיות זקוקות לביט הרצה (execute bit) כדי שהמכולה תוכל לגשת אליהן.

קבצים שמופיעים בבעלות root מעידים על כך שהמכולה הופעלה לפני שהתיקייה המארחת הייתה קיימת, ולכן Docker יצר אותה תחת המשתמש root. עצרו את ה-stack, בצעו chown לתיקייה, והפעילו אותה מחדש.

מחיקת טורנט מ-qBittorrent והיעלמות הקובץ מהספרייה מעידה על כך שהייבוא היה העתק שנמחק לאחר מכן, או שמחקתם את הנתונים עצמם במקום את רשומת הטורנט. בשימוש ב-hardlink תקין, הסרת שם אחד מותירה את השני שלם, כיוון שהנתונים משוחררים רק כאשר מספר הקישורים מגיע לאפס.

דיסק שמתמלא מהר יותר מהמדיה שהוספתם הוא בעיית ההעתקה בצורתה היקרה ביותר. הריצו את בדיקת ה-stat שלעיל לפני שאתם רוכשים שטח אחסון נוסף.

מה מחסנית זו דורשת משרת VPS

שלושת יישומי ה־arr קלים מבחינת משאבים. הם מבצעים polling מול indexers, כותבים למסד נתונים קטן מסוג SQLite ומשנים שמות לקבצים. שרת עם 2 GB של RAM מריץ את ארבע המכולות ללא קושי. העומס מגיע ממקומות אחרים. לקוח הורדות יכול לרתק את פעולות הקלט והפלט של הדיסק בזמן הורדת טורנטים גדולים, ושרת מדיה שמבצע transcoding של וידאו באותו שרת ינצל את המעבד. אחסנו את המדיה על volume עם קצב העברה אמיתי, והגדירו מגבלת רוחב פס בלקוח ההורדות אם השרת מבצע פעולות נוספות שחשובות לכם. הקצו משאבים לפעולות האלה בנפרד, במקום להניח שקיימת יתרת משאבים: סביבת עבודה AFFiNE באירוח עצמי כוללת ארבע מכולות נוספות ומסד נתונים, ובשרת עם 2 GB היא צפויה לצרוך את רוב הזיכרון לעצמה. לא כל שירות נוסף דורש משאבים בהיקף כזה: שירות ייעודי כמו מעקב אימונים openGym באירוח עצמי יכול לחלוק את השרת ללא קושי, כל עוד תגדירו לו TLS משלו ותדעו היכן נמצא קובץ מסד הנתונים שלו לפני שתפקידו בידיו היסטוריית אימונים של שנה. כל שירות שכולל יישום web, מסד נתונים מסוג Postgres ותור משימות רקע נמצא קרוב יותר לקצה של AFFiNE בטווח הזה. לכן החליטו אם מערכת תמיכה Chatwoot באירוח עצמי מתאימה לשרת הזה או שעדיף להפעיל אותה בשרת נפרד, לפני שתגלו את המגבלה באמצע import. יש לנקוט משנה זהירות בעומסי עבודה מתפרצים, משום שהשיא שלהם, ולא הממוצע, הוא שמתנגש ב־import: אם אתם שוקלים OneCLI באירוח עצמי שמקצה לכל אדם agent מבודד משלו, בדקו את נתוני sizing שפורסמו מול המשאבים שבאמת פנויים בזמן ש־qBittorrent פועל בעומס מלא, ולא מול מה ש־free -h מציג בשרת שאינו בעומס.

FAQ

מכיוון שמקור הקבצים והיעד נמצאים על מערכות קבצים שונות מנקודת המבט של המכולה. שני חיבורי bind mounts נפרדים, כמו /downloads ו-/tv, נחשבים לשתי מערכות קבצים שונות גם אם שניהם מגיעים מאותו דיסק מארח. בצעו mount לתיקיית אב אחת כ-/data בכל מכולה, מקמו את ה-downloads וה-library בתוכה, והקישור יהפוך לאפשרי. אשרו את התוצאה באמצעות stat -c '%i %h %n' על שני הקבצים: ה-inode יהיה זהה ומספר הקישורים (link count) יהיה 2.

באילו PUID ו-PGID עלי להשתמש?

השתמשו במזהה המספרי (numeric ID) של חשבון המשתמש במארח שבבעלותו נמצא עץ המדיה, אותו ניתן לקבל באמצעות id -u ו-id -g. בשרת Ubuntu VPS חדש, הערך הוא בדרך כלל 1000 עבור שניהם. כל מכולה ב-stack חייבת להשתמש באותו זוג ערכים, אחרת יישום אחד יכתוב קבצים שאחר לא יוכל לשנות. לאחר שינוי הערכים, צרו מחדש את המכולות בעזרת docker compose up -d --force-recreate ותקנו את הקבצים הקיימים בעזרת chown -R.

האם עלי לחשוף את ממשקי ה-web האלו לאינטרנט?

לא, ומומלץ שלא לעשות זאת. קשרו כל פורט מפורסם ל-127.0.0.1 בקובץ ה-Compose, ולאחר מכן גשו לממשקים דרך מנהרת SSH, VPN, או reverse proxy שמבצע TLS termination ומוסיף אימות משלו. פרסום ישיר שלהם מסוכן יותר ממה שנראה, כיוון ש-Docker מחדיר חוקי firewall משלו וחוק ufw מסוג deny לא יחסום את התעבורה הזו.

היכן אמצא את הסיסמה של qBittorrent?

האימג' של LinuxServer.io מדפיס סיסמה זמנית עבור המשתמש admin בלוג העלייה שלו. הריצו את docker compose logs qbittorrent | grep -i password כדי לקרוא אותה, ולאחר מכן הגדירו סיסמה קבועה תחת Options ו-Web UI. סיסמה זמנית חדשה נוצרת בכל אתחול עד שתגדירו סיסמה משלכם.

האם Jellyfin יכול להשתמש באותן תיקיות?

כן, וזו בדיוק המטרה של המבנה הזה. בצעו mount ל-/mnt/data/media לתוך שרת המדיה שלכם כ-/media, כך שהספריות שלו יימצאו ב-/media/Movies וב-/media/Shows, בזמן ש-Sonarr ו-Radarr כותבים לאותן תיקיות דרך /data/media. תנו לשרת המדיה את אותם PUID ו-PGID כדי שיוכל לקרוא את מה שה-stack של ה-arr כותב.

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