Jellyfin на VPS: власна медіатека онлайн
Розгорніть Jellyfin у Docker на VPS: block storage, права доступу до медіа, direct play і транскодування без GPU, а також безпечний віддалений доступ.
Що ви налаштовуєте
Медіасервер Jellyfin на VPS: один контейнер, три томи та диск block storage із вашими фільмами й серіалами, доступний з будь-якого браузера або застосунку Jellyfin. Для встановлення достатньо compose-файла на п’ятнадцять рядків. Усе, що зазвичай ламається після цього, має дві причини: права доступу до файлів, через які контейнер не може їх читати, і спроба змусити VPS без GPU транскодувати відео, для якого він не призначений. Більша частина цього посібника присвячена саме цим двом проблемам, оскільки саме через них найчастіше звертаються до служби підтримки.
Jellyfin — безкоштовне програмне забезпечення з повністю відкритим вихідним кодом. Для нього не потрібен обліковий запис, платні функції відсутні, а телеметрія не використовується. Саме тому Jellyfin є майже в кожному списку сервісів, які варто розгортати самостійно у 2026 році. Він відтворює медіа, якими ви володієте. Jellyfin не постачається з контентом, і цей посібник не присвячений його отриманню.
Реальність транскодування: перш ніж щось орендувати
Прочитайте це спочатку, адже від цього залежить ваш вибір. Медіасервер виконує одну з двох дій після натискання кнопки відтворення. Direct play передає файл без змін: VPS читає байти з диска й надсилає їх мережею, майже не використовуючи CPU. Transcoding перекодовує відео під час відтворення: змінює роздільну здатність або кодек чи вбудовує субтитри. Це повністю навантажує CPU.
Типовий VPS не має GPU. Тому кожне транскодування виконується на CPU за допомогою libx264/libx265, а програмне кодування потребує значних ресурсів. Одне транскодування 1080p H.264 може повністю завантажити кілька shared vCPU. Транскодування 4K або HEVC зазвичай не встигає за відтворенням у реальному часі. Відтворення зупиняється, а буферизація триває без кінця. Hardware transcoding, завдяки якому це недорого працює на домашньому сервері з Intel iGPU або Nvidia card, недоступне без оренди GPU instances у провайдера.
Тому вся стратегія використання VPS полягає в тому, щоб уникати транскодування. Зберігайте медіатеку в кодеках, які клієнти відтворюють безпосередньо: відео H.264, аудіо AAC або AC3, у контейнері MP4 або MKV. Вибирайте клієнтські застосунки, які підтримують direct play: нативні застосунки Jellyfin для Android TV, iOS і Roku, а також Infuse, Kodi та Jellyfin Media Player для desktop. У такому разі VPS не запускає ffmpeg, і навіть недорогий сервер із 2 vCPU може одночасно передавати відео кільком користувачам. Якщо ви плануєте транскодування, потрібен значно потужніший і дорожчий сервер. Навіть тоді 4K залишається невдалим варіантом.
Окремо розрахуйте пропускну здатність, оскільки це ще один неочевидний фактор. У режимі direct play файл передається зі своїм початковим бітрейтом. Стиснений файл 1080p потребує 8-12 Mbps; Blu-ray remux у 1080p — 20-30 Mbps; 4K HDR — 40-80 Mbps. Троє користувачів, які відтворюють файли з бітрейтом 10 Mbps у режимі direct play, створюють на вашому VPS стабільний вихідний трафік 30 Mbps. Перевірте у своєму тарифі два показники: швидкість порту (чи може він передавати 30 Mbps у вихідному напрямку?) і місячний ліміт трафіку. Один двогодинний фільм із бітрейтом 10 Mbps передає приблизно 9 GB даних, тому тариф із лімітом 1 TB/month дозволяє переглянути трохи більше ста таких фільмів на місяць, або три-чотири на день. Якщо в домогосподарстві переглядають 4K із бітрейтом у чотири-вісім разів вищим, ліміт вичерпується значно швидше. Враховуйте в цьому самому бюджеті весь інший вихідний трафік із того самого сервера, зокрема self-hosted relay RustDesk, який передає весь сеанс віддаленого робочого столу, коли два вузли не можуть з’єднатися напряму.
Передумови
- Новий KVM VPS з Ubuntu 24.04 і доступом root або sudo, а також встановленими Docker і плагіном Compose.
- Том блокового сховища для медіафайлів обсягом, достатнім для вашої бібліотеки (див. нижче про розрахунок). Невеликий кореневий диск, який постачається з VPS, не призначений для зберігання фільмів.
- Доменне ім’я, якщо потрібен публічний доступ через HTTPS, або WireGuard VPN на тому самому VPS, якщо ви хочете залишити всю систему приватною.
- Медіафайли, які ви маєте законне право транслювати: власні ріпи, власні записи та файли, якими ви володієте.
Спочатку підключіть блочне сховище
Підключіть том у панелі керування вашого провайдера, потім знайдіть його та змонтуйте. Визначте ім’я пристрою за допомогою lsblk. Це буде щось на кшталт /dev/sdb або /dev/vdb, але ніколи не кореневий диск.
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 має значення: без нього, якщо блочний том буде від’єднано, сервер не зможе завантажитися та перейде в аварійну оболонку. Найпоширеніша помилка — виконати mkfs.ext4 для тому, на якому вже є дані: ця команда їх видаляє. Форматуйте лише нові томи. Якщо на диску вже зберігається ваша бібліотека, одразу переходьте до рядка для fstab.
Структура медіафайлів, яку очікує Jellyfin
Jellyfin зіставляє метадані за назвами папок і файлів. Якщо структура неправильна, фільми відображатимуться як файли без назви та постера, а епізод може бути зіставлений не з тим серіалом. Потрібно дотримуватися таких правил: кожен фільм має зберігатися у власній папці Name (Year) з відповідною назвою файлу; папки сезонів мають називатися 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 також без проблем проіндексує третю папку з фотографіями, але для фотографій це гірший варіант, ніж спеціалізований сервер. Якщо для вас важливі альбоми, розмістіть їх на окремому сервері з 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: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" фактично задає права доступу до файлів відповідно до вказаного вище власника. /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 почне сканування. Для невеликої медіатеки правильний результат — постери та назви, які з’являються протягом однієї-двох хвилин. Пізніше додавайте або редагуйте медіатеки в розділі Dashboard → Libraries, а для примусового повторного сканування використовуйте Scan All Libraries.
Якщо ви використовуєте транскодування, відкрийте Dashboard → Playback → Transcoding і вкажіть для тимчасових файлів транскодування шлях /cache/transcodes, щоб вони записувалися на том кешу, а не збільшували /config. Для апаратного прискорення залиште значення None, оскільки GPU для прискорення немає.
Віддалений доступ: TLS reverse proxy або доступ лише через VPN
Є два безпечні способи отримувати доступ до Jellyfin ззовні та один небезпечний спосіб, якого слід уникати. Небезпечно публікувати порт 8096 безпосередньо в інтернет: облікові дані передаються відкритим текстом, а порт протягом кількох годин стає ціллю brute-force атак.
Варіант A, TLS reverse proxy. Розмістіть 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, щоб дізнатися про недоступність раніше за ваших глядачів.
Варіант B, доступ лише через VPN. Не публікуйте порт 8096 взагалі; підключайтеся до Jellyfin лише через тунель WireGuard, завершений на цьому самому сервері. Для домашнього використання це найпростіший безпечний варіант: без сертифіката, публічного доступу та поверхні для brute-force атак. Прив’яжіть контейнер до адреси тунелю або localhost і підключайтеся через 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; тег :10 вище залишається в межах major-версії 10.x. Перехід до наступної major-версії потрібно виконувати свідомою зміною тега. Перед цим перегляньте release notes Jellyfin, оскільки під час переходу між major-версіями змінюється схема бібліотеки. Зафіксований тег і один каталог зі станом у резервній копії — це весь необхідний підхід для будь-якого контейнера, що працює постійно. Такий самий принцип дає змогу зберігати пам’ять і розклад 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, після чого повторно проскануйте бібліотеку.
Під час відтворення процесор завантажений на 100%, а відео буферизується. docker stats jellyfin показує завантаження CPU, близьке до 100%, помножених на кількість ядер, а в розділі Dashboard → Playback сеанс має статус Transcode зі швидкістю нижче 1.0x. Клієнт не відтворює медіа напряму, тому VPS виконує транскодування на CPU повільніше за реальний час і не встигає. Причина: непідтримуваний кодек або контейнер, вбудовування субтитрів у відео або перетворення HDR tone mapping. Виправлення: використовуйте клієнт із direct play, зберігайте джерела у форматі H.264/AAC, використовуйте текстові субтитри (SRT), а не графічні (PGS/VOBSUB), оскільки вони потребують вбудовування у відео, і взагалі не використовуйте 4K HDR на сервері лише з CPU.
«Немає сумісних потоків». Повне повідомлення зазвичай має вигляд «Цей клієнт несумісний із медіафайлом, а сервер не надсилає сумісний формат медіа». Клієнт відхилив джерело, а резервне транскодування також не запустилося. Причина: неправильна команда ffmpeg, файл неможливо прочитати або профіль користувача забороняє перетворення відео. Виправлення: прочитайте рядок ffmpeg у Dashboard → Logs, переконайтеся, що файл взагалі відтворюється, перевірте дозволи користувача на відтворення, якщо покладаєтеся на транскодування, і спробуйте другий клієнт, щоб виключити проблеми з кодеками браузера.
У фільмів немає постера або вибрано неправильний постер. Метадані не зіставилися з потрібним записом. Причина: фільм не розміщено у власному каталозі Name (Year), каталог сезону названо 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 не постачається з медіа та не надає способів їх отримання. Це програвач для медіатеки, якою ви вже володієте.