Jellyfin на VPS: власна медіатека онлайн
Розгорніть Jellyfin у Docker на VPS: block storage, права доступу до медіа, direct play і транскодування без GPU, а також безпечний віддалений доступ.
Що ви налаштовуєте
Медіасервер Jellyfin на VPS: один контейнер, три томи та диск block storage із вашими фільмами й серіалами, доступний з будь-якого браузера або застосунку Jellyfin. Для встановлення достатньо compose-файла на п’ятнадцять рядків. Усе, що зазвичай ламається після цього, має дві причини: права доступу до файлів, через які контейнер не може їх читати, і спроба змусити VPS без GPU транскодувати відео, для якого він не призначений. Більша частина цього посібника присвячена саме цим двом проблемам, оскільки саме через них найчастіше звертаються до служби підтримки.
Jellyfin — безкоштовне програмне забезпечення з повністю відкритим вихідним кодом. Для нього не потрібен обліковий запис, платні функції відсутні, а телеметрія не використовується. Саме тому Jellyfin є майже в кожному списку сервісів, які варто розгортати самостійно у 2026 році. Він відтворює медіа, якими ви володієте. Jellyfin не постачається з контентом, і цей посібник не присвячений його отриманню.
Реальність транскодування: перш ніж щось орендувати
Спочатку прочитайте це, оскільки від цього залежить вибір конфігурації. Media server виконує одну з двох дій після натискання кнопки відтворення. Direct play передає файл без змін: VPS зчитує байти з диска та передає їх мережею, майже не використовуючи CPU. Transcoding перекодовує відео під час відтворення: змінює роздільну здатність, кодек або вбудовує субтитри. Це повністю залежить від CPU.
Типовий VPS не має GPU. Тому кожне перекодування відео виконується на CPU за допомогою libx264/libx265, а програмне кодування потребує значних ресурсів. Одне перекодування 1080p H.264 може повністю завантажити кілька спільних vCPU. Перекодування 4K або HEVC зазвичай не забезпечує відтворення в реальному часі, тому відтворення зупиняється та постійно буферизується. Hardware transcoding, завдяки якому це недорого працює на домашньому комп’ютері з Intel iGPU або Nvidia card, недоступне, якщо провайдер не надає в оренду GPU instances.
Тому вся стратегія на VPS зводиться до одного: уникайте transcoding. Зберігайте медіатеку в кодеках, які клієнтські пристрої відтворюють без перекодування: відео H.264, аудіо AAC або AC3, у контейнері MP4 або MKV. Також обирайте клієнтські застосунки з підтримкою direct play: нативні застосунки Jellyfin для Android TV, iOS і Roku, а також Infuse, Kodi та desktop Jellyfin Media Player. У такому разі VPS не запускатиме ffmpeg transcode під час відтворення, а навіть VPS із 2 vCPU зможе одночасно передавати потік кільком користувачам. Якщо ви плануєте transcoding, знадобиться значно потужніший і дорожчий VPS. Навіть тоді 4K буде невдалим вибором.
Також розрахуйте пропускну здатність, оскільки це ще одне важливе обмеження. Direct play передає файл із його власним bitrate. Стиснений файл 1080p використовує 8-12 Mbps, 1080p Blu-ray remux — 20-30 Mbps, а 4K HDR — 40-80 Mbps. Троє користувачів, які відтворюють файли зі швидкістю 10 Mbps у режимі direct play, створюють стабільний вихідний трафік 30 Mbps із вашого VPS. Перевірте у своєму тарифі два значення: швидкість порту (чи може він передавати 30 Mbps у вихідному напрямку?) і місячний ліміт трафіку. Один двогодинний фільм зі швидкістю 10 Mbps передає приблизно 9 GB даних. Тому ліміту 1 TB/month за оплатою за обсяг вистачить трохи більш ніж на сотню таких фільмів на місяць, тобто на три-чотири фільми на день. Якщо домогосподарство переглядає 4K із bitrate у чотири-вісім разів вищим, ліміт буде вичерпано значно швидше. Враховуйте в цьому самому ліміті весь інший вихідний трафік із того самого VPS, зокрема self-hosted relay RustDesk, який передає весь сеанс віддаленого робочого стола, коли два peers не можуть підключитися безпосередньо.
Передумови
- Новий KVM VPS з Ubuntu 24.04 і доступом root або sudo, а також встановленими Docker і плагіном Compose.
- Том блокового сховища для медіафайлів обсягом, достатнім для вашої бібліотеки (див. нижче про розрахунок). Невеликий кореневий диск, який постачається з VPS, не призначений для зберігання фільмів.
- Доменне ім’я, якщо потрібен публічний доступ через HTTPS, або WireGuard VPN на тому самому VPS, якщо ви хочете залишити всю систему приватною.
- Медіафайли, які ви маєте законне право транслювати: власні ріпи, власні записи та файли, якими ви володієте.
Спочатку підключіть block storage
Підключіть volume у панелі свого провайдера, потім знайдіть його та змонтуйте. Отримайте ім’я пристрою з lsblk. Це буде щось на кшталт /dev/sdb або /dev/vdb, але не 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Монтуйте за UUID, а не за /dev/sdb, оскільки літери пристроїв можуть змінюватися після перезавантаження. Інакше можна випадково відформатувати або змонтувати не той диск. Додайте один рядок до /etc/fstab:
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /mnt/media ext4 defaults,nofail 0 2sudo mount -a
df -h /mnt/medianofail має важливе значення: без цього, якщо block volume буде від’єднано, сервер не зможе завантажитися та перейде до emergency shell. Найпоширеніша критична помилка — виконати mkfs.ext4 на volume, який уже містить дані: ця команда стирає їх. Форматуйте лише нові volume. Якщо на диску вже зберігається ваша бібліотека, одразу переходьте до рядка для fstab.
Розкладіть медіафайли відповідно до вимог Jellyfin
Jellyfin зіставляє метадані за назвами папок і файлів. Якщо структуру створено неправильно, фільми відображатимуться як файли без назв і постерів, а епізод може бути зіставлений не з тим серіалом. Майже всю роботу виконують чотири правила: кожен фільм зберігається в окремій папці з відповідною назвою файлу, названою Name (Year) (Jellyfin вважає рік необов’язковим, але з ним зіставлення значно надійніше); папки сезонів мають називатися Season 01, а не S01; для файлів епізодів використовуйте S01E01; спеціальні випуски зберігайте в 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) у назві фільмів — не декоративний елемент. Він розрізняє римейки, щоб система зіставлення вибрала правильну назву. Зберігайте Movies і Shows в окремих папках верхнього рівня, оскільки кожна з них стає бібліотекою Jellyfin для певного типу вмісту, а змішування типів ускладнює роботу постачальника метаданих. Jellyfin також без проблем проіндексує третю папку з фотографіями, але можливості роботи з ними будуть обмеженими порівняно зі спеціалізованим photo server. Тому, якщо ваші альбоми важливі, виділіть для них окремий сервер із PhotoPrism або Immich, а цей сервер використовуйте для фільмів і телебачення.
Права доступу: головна причина, чому бібліотеки залишаються порожніми
Ось помилка, яка може забрати цілий вечір. Офіційний образ jellyfin/jellyfin не враховує змінні середовища PUID/PGID. Вони належать образу LinuxServer.io (lscr.io/linuxserver/jellyfin). В офіційному образі користувача задають ключем user: у compose. Якщо його не вказати, контейнер запускається від імені root. Незалежно від вибраного способу правило однакове: uid/gid, від імені якого працює контейнер, має мати змогу читати кожен каталог із медіафайлами та переходити до нього.
Ми запускатимемо контейнер з uid/gid 1000 — це перший користувач, відмінний від root, у стандартній системі Ubuntu. Перевірте свої значення та призначте власника:
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Каталогам потрібен біт execute (x у 755), а не лише право на читання. Без нього контейнер не може увійти до каталогу, навіть якщо може отримати список його імені. Проблема, через яку вся бібліотека стає порожньою, часто стосується батьківського каталогу: якщо uid контейнера не може перейти до самого mount, він не досягає /media/Movies або /media/Shows. У результаті всі бібліотеки одразу стають порожніми, а в журналі з’являється Access to the path ... is denied. Будь-яка окрема медіапапка, яку контейнер не може прочитати, записується в журнал і пропускається. Тому набір файлів, скопійованих від імені root, може непомітно зникнути з бібліотеки. Саме тому ми рекурсивно змінюємо власника і встановлюємо біт execute для кожного каталогу, а не виправляємо лише одну папку.
Файл docker-compose
services:
jellyfin:
image: jellyfin/jellyfin:12
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Рядок за рядком: jellyfin/jellyfin:12 відстежує поточну гілку випусків 12.x (12.2 станом на 2026-10-10). user: "1000:1000" задає uid/gid, від імені яких працює процес Jellyfin. Це не змінює власника або режим доступу жодного файлу, оскільки Docker зберігає uid/gid хоста для змонтованих каталогів. Саме тому значення chown вище має йому відповідати. /config містить увесь сервер, облікові записи, бібліотеки, метадані та стан перегляду, тому каталог має бути доступним для запису. Саме його потрібно резервно копіювати. /cache — тимчасовий робочий простір, який можна видалити. Монтування медіафайлів виконується як :ro (лише для читання) навмисно: Jellyfin за замовчуванням зберігає обкладинки та метадані в /config, тому йому не потрібно записувати дані до бібліотеки. Режим лише для читання також захищає файли від випадкового видалення або некоректної роботи плагіна. Порт навмисно прив’язано до 127.0.0.1. Вебвхід Jellyfin працює через звичайний HTTP, тому ми не публікуємо порт 8096 в інтернеті. JELLYFIN_PublishedServerUrl — адреса, яку сервер оголошує для локального автоматичного виявлення через UDP-трансляцію в LAN. Клієнти в інтернеті її не бачать і просто використовують URL, який ви вводите в застосунку. Укажіть адресу, яку потрібно передавати клієнтам, і врахуйте, що на віддалених пристроях цей URL потрібно буде ввести вручну.
Запустіть його з каталогу compose:
docker compose up -d
docker logs -f jellyfinПерший запуск: майстер налаштування та ваші бібліотеки
Оскільки порт прив’язаний до localhost, відкрийте майстер через SSH-тунель із ноутбука, а не створюйте виняток у firewall:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipПерейдіть у браузері до http://localhost:8096. Майстер запропонує вибрати мову, а потім створити адміністратора із надійним паролем. Цей обліковий запис керує вашим сервером, тому не використовуйте одноразовий пароль. Додайте першу бібліотеку: виберіть тип вмісту Фільми, вкажіть шлях /media/Movies (шлях усередині контейнера, а не шлях на хості) і повторіть ці дії для типу Серіали зі шляхом /media/Shows. Завершіть налаштування, після чого Jellyfin запустить сканування. Якщо бібліотека невелика, правильний результат — постери й назви з’являться протягом 1–2 хвилин. Пізніше додавайте або редагуйте бібліотеки в Dashboard → Libraries, а для примусового повторного сканування використовуйте Scan All Libraries. Саме ці постери роблять альтернативний frontend цікавим після базового налаштування, оскільки Halcyon відтворює ту саму бібліотеку у форматі відеомагазину 90-х, яким можна прогулюватися на основі зображень, які Jellyfin уже завантажив.
Якщо ви взагалі використовуєте transcoding, відкрийте Dashboard → Playback → Transcoding і вкажіть для тимчасових файлів transcoding шлях /cache/transcodes. Так навантаження припадатиме на cache volume, а не збільшуватиме /config. Залиште апаратне прискорення зі значенням None, оскільки GPU для прискорення немає.
Віддалений доступ: reverse proxy із TLS або доступ лише через VPN
Є два безпечні способи отримати доступ до Jellyfin ззовні та один небезпечний спосіб, якого слід уникати. Небезпечний варіант — напряму опублікувати порт 8096 в інтернеті: дані для входу передаються у відкритому вигляді, а порт протягом кількох годин стає об’єктом brute-force-атак.
Варіант A, reverse proxy із TLS. Розмістіть Jellyfin на піддомені за Traefik з автоматичним TLS для ваших Docker-застосунків або за nginx із сертифікатом Let's Encrypt, виданим Certbot. Jellyfin використовує WebSockets для оновлень у реальному часі, тому проксі має передавати заголовки upgrade. Traefik робить це автоматично; у nginx їх потрібно явно вказати, а для upstream потрібен HTTP/1.1, інакше 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-адресу, щоб дізнатися про простій раніше за глядачів. Спрямуйте його сповіщення на self-hosted ntfy-сервер, і повідомлення про невдалу перевірку надійде push-сповіщенням на телефон, а не електронним листом, який ви прочитаєте наступного ранку. Публікація входу в інтернеті також є слушним моментом для аудиту решти сервера. Не припускайте, що відповідає лише порт 443: sudo ss -tlnp показує всі порти в стані listening і процеси, які їх використовують. Якщо ви також розгортаєте на сервері власний код, open-kritt сканує репозиторії та їхні залежності на наявність вразливостей; він перевіряє вихідний код, а не порти в стані listening.
Варіант B, залишити доступ приватним через VPN. Не публікуйте 8096 в інтернеті взагалі. Отримуйте доступ до Jellyfin лише через тунель WireGuard, що завершується на цьому самому сервері. Для домашнього використання це найпростіший безпечний варіант: без сертифіката, публічної доступності та поверхні для brute-force-атак. Прив’язки до localhost із compose-файлу тут недостатньо, оскільки порт, опублікований на 127.0.0.1, доступний лише з самого VPS, а не з VPN-пірів. Опублікуйте його на tunnel-адресі сервера, наприклад "10.8.0.1:8096:8096", використовуючи власну IP-адресу WireGuard-сервера. Або залиште прив’язку до localhost і розмістіть перед ним проксі, що слухає tunnel-адресу, після чого підключайтеся через VPN. Див. налаштування WireGuard VPN для приватного VPS, щоб налаштувати сам тунель.
Розрахунок сховища та резервні копії
Плануйте обсяг за якістю, а не за кількістю файлів. Стиснені фільми у 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 і папка для transcoding не містять критичних даних, тому їх можна відновити або створити повторно. Медіафайли на /mnt/media резервно копіюйте окремо або вважайте такими, що їх можна повторно оцифрувати. Через великий обсяг більшість користувачів обирає другий варіант. Оновлення виконуються через docker compose pull && docker compose up -d. Тег :12 вище залишається в межах гілки 12.x, тому перехід на наступну major-версію потрібно виконувати свідомо: змініть тег і перед цим перегляньте нотатки до випуску Jellyfin, оскільки під час major-оновлень змінюється схема бібліотеки. У версії 12.0 Jellyfin прибрав початковий "10." із номерів версій (September 2026): 12.0 — це номер, який раніше відповідав би 10.12; версії 11.x не існує, а 10.11.x була останньою гілкою за старою схемою. Якщо у вашому старішому compose-файлі досі вказано jellyfin/jellyfin:10, він зафіксований на 10.11 і ніколи не завантажить 12.x. Цей тег востаннє змінювався 2026-06-06 — на 10.11.11. Заміна його на :12 — це не просто зміна тега: 12.0 змінює схему бази даних і під час першого запуску перезаписує дані. Тому спочатку зупиніть сервер і створіть описану вище резервну копію, оскільки це єдиний спосіб повернутися до попереднього стану. 12.0 можна оновити безпосередньо з 10.10.7 або будь-якого випуску 10.11.x. Після цього потрібне повне сканування бібліотеки, яке триватиме помітно довше, ніж зазвичай. Зафіксований тег і одна резервна копія каталогу стану — це вся схема для будь-якого контейнера, що працює постійно. Такий самий підхід дає змогу зберігати пам’ять і розклад self-hosted агента після перезавантажень.
Режими відмови та повідомлення, які ви побачите
Бібліотека порожня після сканування. У журналі в розділі Dashboard → Logs (або ~/jellyfin/config/log/log_*.log) відображається:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.UID контейнера не може прочитати цей шлях. Причина: медіафайли належать root або UID, відмінному від значення user:, у каталогу немає біта виконання, або батьківське монтування недоступне для переходу з цим UID. Виправте це за допомогою chown -R 1000:1000 /mnt/media, встановіть для каталогів 755, а для файлів 644, після чого повторно запустіть сканування.
Під час відтворення CPU завантажений, а відео буферизується. docker stats jellyfin показує завантаження CPU, близьке до 100%, помножених на кількість ядер, а в розділі Dashboard → Playback сеанс має статус Transcode і швидкість нижчу за 1.0x. Клієнт не відтворює файл безпосередньо, тому VPS виконує транскодування на CPU повільніше за реальний час і не встигає обробляти потік. Причиною може бути непідтримуваний кодек або контейнер, вбудовування субтитрів у відео чи перетворення HDR-тональності. Виправлення: перейдіть на клієнт із direct play, зберігайте джерела у форматі H.264/AAC, використовуйте текстові субтитри (SRT), а не субтитри-зображення (PGS/VOBSUB), які більшість клієнтів Jellyfin не можуть відображати самостійно й тому змушують сервер вбудовувати їх у відео. На сервері лише з CPU взагалі не використовуйте 4K HDR.
«Сумісні потоки недоступні». Повне повідомлення зазвичай має такий вигляд: «This client isn't compatible with the media and the server isn't sending a compatible media format.» Клієнт відхилив джерело, а резервне транскодування також не запустилося. Причиною може бути неправильна команда ffmpeg, файл, який неможливо прочитати, або заборона конвертування відео в профілі користувача. Перегляньте рядок ffmpeg у розділі Dashboard → Logs, переконайтеся, що файл взагалі відтворюється, перевірте дозволи користувача на відтворення, якщо покладаєтеся на транскодування, і спробуйте другий клієнт, щоб виключити особливості підтримки кодеків у браузері.
У фільмів немає постера або вибрано неправильний постер. Метадані не зіставилися з потрібним записом. Причиною може бути каталог або ім’я файлу, які не відповідають назві в базі постачальника метаданих, римейк або поширена назва без року, за яким можна розрізнити версії, каталог сезону з назвою S01 замість Season 01 або епізоди не у форматі S01E01. Перейменуйте файли відповідно до наведеної вище структури, потім виберіть Refresh metadata → Replace all або використайте Identify для окремого елемента, щоб вказати правильний запис TMDB/TVDB.
FAQ
Чи може VPS транскодувати відео без GPU?
Так, але лише на CPU, і це потребує значних ресурсів. Одне програмне транскодування 1080p може повністю завантажити кілька vCPU, а для 4K або HEVC зазвичай не вдається підтримувати швидкість реального часу, тому відтворення буферизується. Найкраще уникати транскодування: зберігайте медіатеку в H.264/AAC і використовуйте клієнтські застосунки з direct play, щоб VPS лише передавав байти. Орендуйте інстанс із GPU лише тоді, коли вам справді потрібне транскодування під час відтворення.
Чому моя медіатека Jellyfin порожня після сканування?
Майже завжди причина полягає в правах доступу. Офіційний образ jellyfin/jellyfin запускається від імені користувача, якого ви вказали в user:, або від імені root. Якщо цей uid не має права читати файли, сканування записує в журнал Access to the path ... is denied і пропускає їх. Виправте власника за допомогою chown -R 1000:1000 /mnt/media, надайте каталогам право виконання за допомогою 755, повторіть сканування та перевірте також батьківський каталог. Якщо uid контейнера не може пройти через сам /mnt/media, він не досягне каталогів медіатеки, і вона залишиться порожньою. Друга за поширеністю причина — структура каталогів не відповідає очікуванням Jellyfin.
Як безпечно отримати віддалений доступ до Jellyfin?
Є два належні варіанти. Розмістіть його за TLS reverse proxy на піддомені, щоб вхід і потокове передавання були зашифровані, та додайте fail2ban. Ніколи не відкривайте звичайний порт 8096, оскільки він передає пароль у відкритому вигляді. Або залиште сервіс повністю приватним і підключайтеся до нього лише через VPN. Для домашнього використання це найпростіший безпечний варіант. Вказуйте в застосунках публічну адресу безпосередньо: autodiscovery використовує широкомовні повідомлення локальної мережі, тому вони не досягають клієнтів, які підключаються через інтернет.
Скільки дискового простору та пропускної здатності потрібні VPS для Jellyfin?
Обсяг диска залежить від якості: передбачайте 4-15 GB на стиснений фільм у 1080p, 20-40 GB на remux і 40-100 GB на 4K. Тому для більшості медіатек потрібен block volume на 2-4 TB. Потрібна пропускна здатність визначається бітрейтом direct play: 8-12 Mbps на один потік 1080p і значно більше для 4K. Переконайтеся, що швидкості порту достатньо для запланованої кількості одночасних глядачів, і контролюйте місячний ліміт переданих даних. Якщо плануєте транскодування, залиште запас CPU. Якщо плануєте direct play, надавайте перевагу пропускній здатності, а не кількості ядер.
Чи законно запускати Jellyfin на VPS?
Сам Jellyfin є безкоштовним програмним забезпеченням із відкритим кодом, і його запуск повністю законний. Важливим є вміст: передавайте лише медіа, якими ви володієте або які маєте право зберігати, зокрема власні копії дисків, записи чи інші файли з відповідним дозволом. Jellyfin не постачається з медіа та не надає способів їх отримання. Це програвач для медіатеки, якою ви вже володієте.