SSD Nodes Learn
מדריכים Matt Connorמאת Matt Connor · עודכן 2026-07-24

להתקין Jellyfin ב-VPS עם Docker

מדריך להרצת Jellyfin ב-Docker על VPS. נלמד על שימוש ב-block storage, הרשאות קבצים ופתרון בעיות CPU transcoding ללא GPU בשרת המדיה שלכם.

מה אתם בונים

שרת מדיה Jellyfin על VPS: קונטיינר אחד, שלושה volumes, וכונן block-storage עבור הסרטים והתוכניות שלכם, הנגיש מכל דפדפן או אפליקציית Jellyfin. ההתקנה מבוצעת באמצעות קובץ compose בן 15 שורות. כל תקלה שתתרחש לאחר מכן נובעת משני גורמים — הרשאות קבצים שקונטיינר אינו יכול לקרוא, או בקשה מ-VPS ללא GPU לבצע transcoding לווידאו, פעולה שאין לו את היכולת לבצע. מדריך זה מתמקד ברוב אורכו בשני הגורמים הללו, כיוון שהם המקור העיקרי לכרטיסי התמיכה.

Jellyfin הוא תוכנה חופשית וקוד פתוח לחלוטין, ללא צורך בחשבון, ללא תכונות בתשלום וללא טלמטריה — הסיבה שהוא מופיע כמעט בכל רשימה של דברים ששווה לארח בעצמכם ב-2026. הוא מנגיש מדיה שבבעלותכם. הוא אינו מספק תוכן, ומדריך זה אינו עוסק בהשגת תוכן כלשהו.

מציאות ה-transcoding, לפני ששוכרים כל דבר

קראו זאת תחילה, כיוון שזה משנה את מה שתקנו. שרת מדיה מבצע אחת משתי פעולات כאשר לוחצים על play. Direct play משדר את הקובץ כפי שהוא: ה-VPS קורא bytes מהדיסק ושולח אותם ברשת, מה שאינו צורך כמעט CPU. Transcoding מבצע re-encode לווידאו בזמן אמת — רזולוציה חדשה, codec חדש, או כיתתיות שמוטמעות בתוך הווידאו — וזו עבודה שדורשת CPU מלא.

ל-VPS טיפוסי אין GPU. לכן כל transcode מתבצע על ה-CPU עם libx264/libx265, וקידוד תוכנה (software encoding) הוא יקר. transcode יחיד של 1080p H.264 יכול להגיע לרוויה של מספר vCPUs משותפים; transcode של 4K או HEVC בדרך כלל אינו מסוגל לעמוד בקצב זמן אמת, ולכן הניגון נתקע ומת buffering לנצח. transcoding חומרתי — הדבר שהופך את התהליך לזול בשרת ביתי עם Intel iGPU או כרטיס Nvidia — פשוט אינו זמין עבורכם אלא אם הספק שלכם משכיר instances עם GPU.

לכן, האסטרטגיה המלאה ב-VPS היא: להימנע מ-transcoding. שמרו את הספרייה שלכם ב-codecs שהלקוחות שלכם מנגנים באופן טבעי (natively) — וידאו H.264, אודיו AAC או AC3, בתוך container מסוג MP4 או MKV — ובחרו אפליקציות לקוח שתומכות ב-direct-play: אפליקציות Jellyfin המקוריות עבור Android TV, iOS ו-Roku, בנוסף ל-Infuse, Kodi, ו-Jellyfin Media Player למחשב. אם תעשו זאת, ה-VPS לעולם לא יצטרך להשתמש ב-ffmpeg, ושרת צנוע עם 2 vCPU יוכל לשדר לכמה אנשים בו-זמנית. אם תתכננו ל-transcoding, תזדקקו לשרת גדול ויקר הרבה יותר, וגם אז 4K הוא הימור רע.

חשבו גם על רוחב הפס (bandwidth), כיוון שזהו ההפתעה השנייה. direct play שולח את הקובץ ב-bitrate המקורי שלו. קובץ 1080p דחוס רץ ב-8-12 Mbps; 1080p Blu-ray remux ב-20-30 Mbps; 4K HDR ב-40-80 Mbps. שלושה אנשים שמבצעים direct-play לקבצים של 10 Mbps דורשים 30 Mbps של upload רציף מה-VPS שלכם. בדקו שני מספרים בתוכנית שלכם: מהירות ה-port (האם הוא יכול להזרים 30 Mbps במעלה - upstream?) ומכסת התעבורה החודשית. סרט אחד של שעתיים ב-10 Mbps הוא כ-9 GB של יציאה, לכן מכסה של 1 TB לחודש מאפשרת מעט יותר ממאה סרטים כאלה בחודש — שלושה או ארבעה ביום — ומשק בית שצופה ב-4K, ב-4 עד 8 เท่า מה-bitrate, יצרוך את המכסה הרבה יותר מהר.

Prerequisites

  • שרת Ubuntu 24.04 KVM VPS חדש עם הרשאות root או sudo, ועם Docker ו-Compose plugin מותקנים.
  • נפח אחסון (block-storage) עבור המדיה, בגודל המתאים לספרייה שלך (ראה פירוט גודל בהמשך). אין לשמור את הסרטים על דיסק ה-root הקטן שמגיע עם ה-VPS.
  • שם דומיין אם ברצונך לאפשר גישה HTTPS ציבורית, או WireGuard VPN על אותו ה-VPS אם ברצונך לשמור על המערכת פרטית לחלוטין.
  • מדיה שיש לך זכות חוקית להזרים — עותקים (rips) משלך, הקלטות משלך, או קבצים שבבעלותך.

Mount the block storage first

Attach the volume in your provider's panel, then find it and mount it. Get the device name from lsblk — it will be something like /dev/sdb or /dev/vdb, never the root disk.

lsblk
sudo mkfs.ext4 /dev/sdb          # ONLY on a new, empty volume — this ERASES it
sudo mkdir -p /mnt/media
sudo blkid /dev/sdb              # copy the UUID shown for this device

Mount it by UUID, not by /dev/sdb, because device letters reorder across reboots and you can end up formatting or mounting the wrong disk. Add one line to /etc/fstab:

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  /mnt/media  ext4  defaults,nofail  0  2
sudo mount -a
df -h /mnt/media

nofail matters: without it, if the block volume is ever detached the box refuses to boot and drops to an emergency shell. The single biggest mistake here is running mkfs.ext4 on a volume that already holds data — it wipes it. Format new volumes only; if the disk already has your library on it, skip straight to the fstab line.

ארגנו את המדיה כפי ש-Jellyfin מצפה

Jellyfin מתאימה מטא-דאטה לפי שמות תיקיות וקבצים. ארגון שגוי יגרום לכך שסרטים יופיעו כקבצים ללא שם וללא פוסטר, או שפרק יתאים לסדרה הלא נכונה. קיימים שלושה כללים בלבד: כל סרט חייב להיות בתוך תיקיית Name (Year) משלו עם שם קובץ תואם; תיקיות עונות נקראות Season 01 ולא S01; קבצי פרקים משתמשים ב-S01E01; ופרקים מיוחדים (specials) נמצאים ב-Season 00.

/mnt/media
├── Movies
│   ├── Blade Runner (1982)
│   │   └── Blade Runner (1982).mkv
│   └── Arrival (2016)
│       └── Arrival (2016).mkv
└── Shows
    └── Severance (2022)
        ├── Season 01
        │   ├── Severance - S01E01.mkv
        │   └── Severance - S01E02.mkv
        └── Season 00
            └── Severance - The Lexington Letter.mkv

ה-(Year) בסרטים אינו רק לקישוט — הוא נועד להבחין בין גרסאות שונות (remakes) כדי שהמערכת תמשוך את הכותרת הנכונה. שמרו על Movies ו-Shows כתיקיות נפרדות ברמה העליונה (top-level), כיוון שכל אחת מהן הופכת לספריית Jellyfin מסוג תוכן ספציפי, וערבוב ביניהן יבלבל את ספק המטא-דאטה.

Permissions: הסיבה העיקרית לכך שספריות מופיעות ריקות

להלן הטעות הנפוצה שגורמת לאובדן זמן יקר. ה-image הרשמי של jellyfin/jellyfin אינו מכבד את משתני הסביבה PUID/PGID — משתנים אלו שייכים ל-image של LinuxServer.io (lscr.io/linuxserver/jellyfin). ב-image הרשמי, ניתן לשלוט במשתמש באמצעות מפתח user: ב-compose, ואם לא מגדירים אותו, ה-container ירוץ כ-root. בכל מקרה, הכלל זהה: ה-uid/gid שבו רץ ה-container חייב להיות בעל הרשאה לקרוא ולעבור (traverse) בכל ספריית המדיה.

אנחנו נרוץ עם uid/gid 1000, המשתמש הראשון שאינו root במערכת Ubuntu סטנדרטית. ודאו את המשתמש שלכם והגדירו בעלות (ownership):

id                                  # confirm your user is uid=1000 gid=1000
sudo chown -R 1000:1000 /mnt/media
sudo find /mnt/media -type d -exec chmod 755 {} \;
sudo find /mnt/media -type f -exec chmod 644 {} \;
mkdir -p ~/jellyfin/config ~/jellyfin/cache
sudo chown -R 1000:1000 ~/jellyfin

תיקיות זקוקות ל-bit של execute (ה-x ב-755), ולא רק לקריאה — ללא הרשאה זו, ה-container לא יוכל להיכנס לתיקייה למרות שהוא יכול להציג את שמה. המלכודת שגורמת לספרייה שלמה להופיע ריקה היא תיקיית האם: אם ה-uid של ה-container לא יכול לעבור דרך נקודת העגינה (mount) עצמה, הוא לעולם לא יגיע ל-/media/Movies או /media/Shows, וכל הספרייה תופיע ריקה בבת אחת עם הודעת Access to the path ... is denied בלוג. כל תיקיית מדיה בודדת שלא ניתן לקרוא לה תתועד ותדלג, ולכן קבוצת קבצים שהועתקה כ-root תיעלם מהספרייה ללא התראה. זו הסיבה שאנו מבצעים chown באופן רקורסיבי ומגדירים את ה-execute bit בכל תיקייה, במקום לתקן תיקייה אחת בלבד.

הקובץ docker-compose

services:
  jellyfin:
    image: jellyfin/jellyfin:10
    container_name: jellyfin
    user: "1000:1000"
    restart: unless-stopped
    ports:
      - "127.0.0.1:8096:8096"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /mnt/media:/media:ro
    environment:
      - JELLYFIN_PublishedServerUrl=https://jellyfin.example.com

שורה אחר שורה: user: "1000:1000" הוא זה שקובע בפועל את הרשאות הקובץ, בהתאם לבעלות (ownership) המופיעה לעיל. /config מכיל את כל השרת — חשבונות, ספריות, מטא-דאטה ומצב מעקב (watch state) — לכן הוא חייב להיות ניתן לכתיבה והוא הפריט שאתם מגבים. /cache הוא שטח עבודה זמני. נקודת ההרכבה (mount) של המדיה היא :ro (קריאה בלבד) בכוונה: Jellyfin שומר כברירת מחדל artwork ומטא-דאטה תחת /config, ולכן הוא לעולם אינו זקוק לכתיבה לספרייה שלכם. מצב קריאה בלבד מגן על הקבצים שלכם מפני מחיקה בשוגג או תוסף (plugin) תקול. הפורט קשור ל-127.0.0.1 בכוונה — ממשק ה-web login של Jellyfin הוא ב-HTTP פשוט, לכן איננו חושפים את פורט 8096 לאינטרנט הציבורי. JELLYFIN_PublishedServerUrl הוא הכתובת שהשרת מפרסם לצורך גילוי אוטומטי מקומי (local autodiscovery) — שידור UDP ברשת LAN, כך שמשתמשים באינטרנט לעולם לא יראו אותה וישתמשו פשוט ב-URL שאתם מקלידים באפליקציה. הגדירו זאת לכתובת שבה הלקוחות אמורים להשתמש, וצפו להקליד את ה-URL הזה ידנית במכשירים מרוחקים.

הפעילו את השרת מתוך ספריית ה-compose:

docker compose up -d
docker logs -f jellyfin

הרצה ראשונה: אשף ההתקנה והספריות שלך

מכיוון שהפורט מקושר ל-localhost, יש לגשת לאשף באמצעות tunnel של SSH מהלפטופ במקום לפתוח חור בחומת האש:

ssh -L 8096:127.0.0.1:8096 you@your-vps-ip

כעת עבור אל http://localhost:8096. האשף יוביל אותך לבחירת שפה, ולאחר מכן ליצירת משתמש admin עם סיסמה חזקה — חשבון זה הוא השרת שלך, לכן אל תשתמש בסיסמה זמנית. הוסף את הספרייה הראשונה שלך: בחר סוג תוכן Movies, כוון אל /media/Movies (הנתיב בתוך ה-container, לא הנתיב של ה-host), וחזור על הפעולה עבור Shows ב-/media/Shows. לאחר הסיום, Jellyfin יבצע סריקה. תוצאה תקינה היא שפוסטרים וכותרים יופיעו תוך דקה או שתיים עבור ספרייה קטנה. ניתן להוסיף או לערוך ספריות מאוחר יותר תחת Dashboard → Libraries, ולהפעיל סריקה מחדש באמצעות Scan All Libraries.

אם אתה משתמש ב-transcoding, פתח את Dashboard → Playback → Transcoding והגדר את נתיב ה-temp של ה-transcode ל-/cache/transcodes, כדי שהעומס יישמר על ה-cache volume במקום לנפח את /config. השאר את ה-hardware acceleration על None — אין GPU להאצה.

גישה מרחוק: TLS reverse proxy, או שמירה בתוך ה-VPN

קיימות שתי דרכים בטוחות לגשת ל-Jellyfin מבחוץ, ודרך אחת לא בטוחה שיש להימנע ממנה. הדרך הלא בטוחה היא פתיחת port 8096 ישירות לאינטרנט: פרטי ההתחברות נשלחים בטקסט גלוי (cleartext) והפורט יהיה חשוף להתקפות brute-force תוך שעות.

אפשרות A — TLS reverse proxy. מקמו את Jellyfin תחת subdomain מאחורי Traefik עם TLS אוטומטי עבור אפליקציות Docker שלך, או מאחורי nginx עם תעודת Let's Encrypt שהונפקה על ידי Certbot. Jellyfin משתמש ב-WebSockets עבור עדכונים בזמן אמת, לכן ה-proxy חייב להעביר את ה-upgrade headers. Traefik מבצע זאת באופן אוטומטי; ב-nginx יש להגדיר אותם במפורש, ונדרש להשתמש ב-HTTP/1.1 מול ה-upstream כדי שה-upgrade יתבצע:

location / {
    proxy_pass http://127.0.0.1:8096;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

הגדירו את JELLYFIN_PublishedServerUrl לכתובת ה-https:// כדי שכל תהליך autodiscovery מקומי יפרסם את ה-URL הנכון — אפליקציות מרחוק משתמשות בכתובת שתספקו להן — והוסיפו את fail2ban כדי להאט ניסיונות brute-force נגד ההתחברות. ברגע שהשרת חשוף לציבור, הפנו את Uptime Kuma ל-URL כדי לקבל התראה על השבתה לפני הצופים שלך.

אפשרות B — שמירה פרטית בתוך VPN. אל תפרסמו את port 8096 כלל; גשו ל-Jellyfin רק דרך מנהרת WireGuard המסתיימת באותה מכונה. עבור משק בית, זו הבחירה המאובטחת והפשוטה ביותר — ללא תעודה, ללא חשיפה ציבורית, וללא שטח תקיפה ל-brute-force. קשרו (Bind) את ה-container לכתובת ה-tunnel או ל-localhost והתחברו דרך ה-VPN. עיינו ב-הגדרת WireGuard VPN עבור VPS פרטי לצורך הקמת המנהרה עצמה.

Storage sizing and backups

תכנן את התקציב לפי איכות הקובץ, לא לפי מספר הקבצים. סרטי 1080p דחוסים תופסים בין 4 ל-15 GB כל אחד; remux ב-1080p תופס 20-40 GB; עונה של סדרת טלוויזיה ב-1080p תופסת 15-40 GB; כל תוכן ב-4K תופס 40-100 GB לסרט. ספרייה של כמה מאות סרטים בתוספת כמה סדרות דורשת נפח של 2-4 TB. עדיף להקצות נפח עודף ב-block volume פעם אחת מאשר לבצע הגירה מאוחרת.

/config מייצג את מצב השרת המלא, ולכן זהו הרכיב היחיד שחובה לגבות. בצע snapshot או הפסק את השירות והשתמש ב-tar, ושמור את העתק מחוץ למכשיר:

docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d

/cache ותיקיית ה-transcode הם זמניים וניתנים למחיקה. את המדיה ב-/mnt/media יש לגבות בנפרד או להתייחס אליה ככזו שניתן להפיק מחדש (re-rippable) — רוב המשתמשים בוחרים באפשרות השנייה בשל הגודל. שדרוגים הם docker compose pull && docker compose up -d; התג (tag) של :10 נשאר בתוך גרסת ה-10.x הראשית, לכן מעבר לגרסה ראשית הבאה דורש עריכה מכוונת של התג — יש לקרוא את ה-release notes של Jellyfin לפני ביצוע השינוי, כיוון ששינויי סכימת ספרייה (library schema migrations) מתרחשים בגרסאות ראשיות.

Failure modes, with the strings you will see

Library is empty after a scan. הלוג ב-Dashboard → Logs (או ~/jellyfin/config/log/log_*.log) מציג:

System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.

ה-uid של ה-container אינו יכול לקרוא את הנתיב. הסיבה: המדיה בבעלות root או uid שונה מהערך user: שלך, ספרייה ללא execute bit, או שה-mount ה-parent אינו ניתן למעבר על ידי ה-uid הזה. פתרון: chown -R 1000:1000 /mnt/media, ספרייה 755, קבצים 644, ולאחר מכן rescan.

Playback pins the CPU and buffers. הנתון docker stats jellyfin מציג שימוש ב-CPU קרוב ל-100% כפול מספר הליבות שלך, ו-Dashboard → Playback מציג את ה-session כ-Transcode עם מהירות נמוכה מ-1.0x. ה-client אינו מבצע direct-play, לכן ה-VPS מבצע CPU-transcoding לאט יותר מהזמן הממשי ומתקשה לעמוד בקצב. הסיבה: codec או container לא נתמכים, subtitle burn-in, או HDR tone-mapping. פתרון: עבור ל-client בתצורת direct-play, שמור מקורות ב-H.264/AAC, השתמש ב-subtitles מסוג text (SRT) במקום image (PGS/VOBSUB) שמכריחים burn-in, ואל תשתמש ב-4K HDR במכשיר המבוסס על CPU בלבד.

"No compatible streams are available." ההודעה המלאה היא בדרך כלל "This client isn't compatible with the media and the server isn't sending a compatible media format." ה-client דחה את המקור, וגם ה-fallback transcode נכשל בהפעלה. הסיבה: פקודת ffmpeg שגויה, קובץ שאינו ניתן לקריאה, או פרופיל משתמש החוסם המרת וידאו. פתרון: קרא את שורת ה-ffmpeg ב-Dashboard → Logs, ודא שהקובץ מתנגן בכלל, בדוק את הרשאות ה-playback של המשתמש אם אתה מסתמך על transcoding, ונסה client נוסף כדי לשלול בעיות codec בדפדפן.

Films have no poster or the wrong one. המטא-דאטה לא תאם את המדיה. הסיבה: סרט שאינו בתוך תיקיית ה-Name (Year) שלו, תיקיית עונה בשם S01 במקום Season 01, פרקים שאינם בפורמט S01E01, או שנה חסרה. פתרון: שנה את השמות למבנה שצוין לעיל, ולאחר מכן Refresh metadata → Replace all, או השתמש ב-Identify על פריט בודד כדי לקשר את רשומה הנכונה מ-TMDB/TVDB.

FAQ

האם VPS יכול לבצע transcoding לווידאו ללא GPU?

כן, אך הדבר מתבצע באמצעות ה-CPU בלבד, וזה יקר. transcoding אחד בתוכנה ברזולוציית 1080p יכול להעמיס על מספר vCPUs, ובדרך כלל 4K או HEVC לא יצליחו לעמוד בקצב זמן אמת, מה שיגרום ל-buffering. הפתרון הטוב ביותר הוא להימנע מ-transcoding: שמרו את הספרייה בפורמט H.264/AAC והשתמשו באפליקציות לקוח התומכות ב-direct-play, כך שה-VPS ישדר bytes בלבד. שכרו instance עם GPU רק אם אתם זקוקים فعلاً ל-transcoding בזמן אמת.

מדוע ספריית Jellyfin שלי ריקה לאחר סריקה?

ברוב המקרים מדובר בהרשאות. ה-image הרשמי של jellyfin/jellyfin רץ תחת ה-user: שהגדרתם (או כ-root), ואם הקבצים אינם ניתנים לקריאה על ידי ה-uid הזה, ה-scan logs Access to the path ... is denied ומדלג עליהם. תקנו את הבעיה באמצעות chown -R 1000:1000 /mnt/media, תנו לתיקיות את ה-execute bit (755), ובצעו סריקה מחדש — בדקו גם את תיקיית האב, מכיוון שאם ה-uid של ה-container אינו יכול לעבור דרך /mnt/media עצמה, הוא לעולם לא יגיע לתיקיות הספרייה והכל יופיע כריק. הסיבה השנייה הנפוצה ביותר היא מבנה תיקיות שאינו תואם למה ש-Jellyfin מצפה לו.

כיצד אוכל לגשת ל-Jellyfin מרחוק ובאופן מאובטח?

ישנן שתי אפשרויות טובות. ניתן להציב אותו מאחורי TLS reverse proxy תחת subdomain כדי שההתחברות והשידור יהיו מוצפנים, ולהוסיף fail2ban — לעולם אל תחשפו את port 8096 ללא הצפנה, שכן הוא שולח את הסיסמה ב-cleartext. לחלופין, ניתן להשאיר אותו פרטי לחלוטין ולגשת אליו רק דרך VPN, שזהו הבחירה הבטוחה והפשוטה ביותר לבית. תנו לאפליקציות את הכתובת הציבורית ישירות — autodiscovery מבוסס על broadcast ברשת המקומית, ולכן הוא אינו מגיע ללקוחות המגיעים דרך האינטרנט.

כמה שטח דיסק ורוחב פס דרושים ל-Jellyfin VPS?

שטח הדיסק תלוי באיכות: הקצו 4-15 GB לכל סרט 1080p דחוס, 20-40 GB לכל remux, ו-40-100 GB עבור 4K, לכן רוב הספרייה זקוקה לנפח של 2-4 TB ב-block volume. רוחב הפס נקבע לפי ה-bitrate של ה-direct-play — 8-12 Mbps לכל stream של 1080p, ויותר משמעותית עבור 4K — לכן ודאו שמהירות ה-port שלכם מספיקה למספר הצופים בו-זמנית ושימו לב למכסת התעבורה החודשית. הוסיפו headroom ל-CPU אם אתם מתכננים לבצע transcoding; אם אתם מתכננים להשתמש ב-direct-play, תנו עדיפות לרוחב הפס על פני ליבות.

האם זה חוקי להריץ Jellyfin על VPS?

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

#jellyfin#media-server#docker#self-hosting#transcoding