Как установить Jellyfin на VPS в Docker
Пошаговое руководство по запуску Jellyfin на VPS. Разбираем настройку прав доступа к томам, оптимизацию транскодирования без GPU и безопасный удаленный доступ к медиатеке.
Что вы создаете
Медиасервер Jellyfin на VPS: один контейнер, три тома и блочное хранилище для ваших фильмов и сериалов, доступное из любого браузера или приложения Jellyfin. Установка выполняется с помощью compose-файла из 15 строк. Все последующие проблемы возникают по двум причинам: права доступа к файлам, которые контейнер не может прочитать, и попытки транскодирования видео на VPS без GPU, который для этого не предназначен. Большая часть этого руководства посвящена именно этим двум аспектам, так как именно они являются основными источниками обращений в поддержку.
Jellyfin — это бесплатное ПО с полностью открытым исходным кодом, без обязательных учетных записей, платных функций и телеметрии. Именно поэтому он входит почти в каждый список проектов для self-hosting в 2026 году. Он воспроизводит медиафайлы, которыми вы владеете. Продукт не поставляется с контентом, и данное руководство не касается вопросов его получения.
Реальность транскодирования: что нужно знать перед арендой
Прочитайте это до покупки, так как информация влияет на выбор оборудования. При нажатии кнопки воспроизведения медиасервер выполняет одно из двух действий. Direct play передает файл «как есть»: VPS считывает байты с диска и отправляет их по сети, что почти не нагружает CPU. Транскодирование перекодирует видео на лету — меняет разрешение, кодек или «вшивает» субтитры, что требует значительных ресурсов CPU.
Обычный VPS не имеет GPU. Поэтому любое транскодирование видео выполняется на CPU с помощью libx264/libx265, а программное кодирование — ресурсоемкая задача. Одиночный поток транскодирования 1080p H.264 может полностью загрузить несколько общих vCPU; транскодирование 4K или HEVC обычно не успевает за реальным временем, из-за чего воспроизведение постоянно прерывается на буферизацию. Аппаратное транскодирование, которое делает этот процесс дешевым на домашнем ПК с Intel iGPU или картой Nvidia, недоступно, если провайдер не предоставляет GPU-инстансы.
Поэтому стратегия работы на VPS проста: избегайте транскодирования. Храните библиотеку в кодеках, которые клиенты воспроизводят нативно: видео H.264, аудио AAC или AC3 в контейнерах MP4 или MKV. Используйте клиентские приложения с поддержкой direct play: нативные приложения Jellyfin для Android TV, iOS и Roku, а также Infuse, Kodi и десктопный Jellyfin Media Player. При таком подходе VPS не придется запускать ffmpeg для транскодирования, и даже скромный сервер с 2 vCPU сможет обслуживать несколько пользователей одновременно. Если вы планируете транскодировать, вам потребуется гораздо более мощный и дорогой сервер, но даже в этом случае 4K остается рискованным выбором.
Также рассчитайте пропускную способность, так как это еще один важный фактор. При direct play файл передается с собственным битрейтом. Сжатый файл 1080p требует 8-12 Mbps; ремукс Blu-ray 1080p — 20-30 Mbps; 4K HDR — 40-80 Mbps. Три пользователя, смотрящие файлы по 10 Mbps через direct play, создают постоянную исходящую нагрузку на VPS в 30 Mbps. Проверьте два параметра вашего тарифа: скорость порта (может ли он отдавать 30 Mbps?) и месячный лимит трафика. Двухчасовой фильм с битрейтом 10 Mbps — это около 9 GB данных. Лимит в 1 TB в месяц позволит посмотреть чуть более сотни таких фильмов, то есть три-четыре в день. Просмотр 4K-контента с битрейтом в четыре-восемь раз выше исчерпает лимит гораздо быстрее. Учитывайте и другие сервисы, потребляющие исходящий трафик с того же сервера, включая собственное реле RustDesk, которое передает весь сеанс удаленного рабочего стола, если два узла не могут соединиться напрямую.
Предварительные требования
- Свежий VPS на базе Ubuntu 24.04 с KVM, доступом 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 имеет значение: без него, если блочный том будет отсоединен, сервер откажется загружаться и перейдет в аварийную оболочку (emergency shell). Самая распространенная ошибка здесь — запуск mkfs.ext4 на томе, где уже есть данные; это приведет к их удалению. Форматируйте только новые тома; если на диске уже находится ваша библиотека, сразу переходите к строке в 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 без проблем проиндексирует и третью папку с фотографиями, но функциональность будет ограничена по сравнению со специализированным сервером для фото. Если ваши альбомы важны, выделите для них отдельный сервер с 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 контейнера не может выполнить обход самой точки монтирования, он никогда не доберется до /media/Movies или /media/Shows, и вся библиотека мгновенно окажется пустой, а в логах появится Access to the path ... is denied. Любая отдельная папка с медиафайлами, которую контейнер не может прочитать, будет пропущена с записью в лог, поэтому пакет файлов, скопированный от имени root, просто исчезнет из библиотеки. Именно поэтому мы выполняем chown рекурсивно и устанавливаем бит 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 (на 2026-10-10 это 12.2). 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-широковещание в локальной сети, поэтому клиенты из интернета его не видят и просто используют URL, который вы вводите в приложении. Укажите адрес, который должны знать клиенты, и будьте готовы вводить этот URL вручную на удаленных устройствах.
Запустите сервис из директории с compose-файлом:
docker compose up -d
docker logs -f jellyfinПервый запуск: мастер настройки и ваши библиотеки
Поскольку порт привязан к localhost, получите доступ к мастеру настройки через SSH-туннель с вашего ноутбука, вместо того чтобы открывать порт в брандмауэре:
ssh -L 8096:127.0.0.1:8096 you@your-vps-ipТеперь перейдите по адресу http://localhost:8096. Мастер настройки предложит выбрать язык, а затем создать учетную запись администратора с надежным паролем. Эта учетная запись управляет вашим сервером, поэтому не используйте временные или простые пароли. Добавьте свою первую библиотеку: выберите тип контента Movies, укажите путь /media/Movies (путь внутри контейнера, а не путь на хосте), и повторите то же самое для Shows, указав /media/Shows. Завершите настройку, и Jellyfin начнет сканирование. При успешном выполнении через минуту или две для небольшой библиотеки появятся постеры и названия. Добавлять или изменять библиотеки можно позже в разделе Dashboard → Libraries, а принудительное повторное сканирование запускается кнопкой Scan All Libraries. Эти постеры также делают альтернативный интерфейс интересным вариантом после настройки базового функционала, так как Halcyon перестраивает ту же библиотеку в виде интерактивного видеосалона 90-х на основе графических материалов, которые Jellyfin уже загрузил.
Если вы планируете использовать транскодирование, откройте Dashboard → Playback → Transcoding и установите путь для временных файлов транскодирования /cache/transcodes, чтобы нагрузка приходила на кэш-том, а не переполняла /config. Оставьте аппаратное ускорение в значении None, так как графический процессор для ускорения отсутствует.
Удаленный доступ: TLS reverse proxy или использование VPN
Существует два безопасных способа доступа к Jellyfin извне и один небезопасный, которого следует избегать. Небезопасный способ — публикация порта 8096 напрямую в интернет: данные для входа передаются в открытом виде, а порт подвергается брутфорс-атакам в течение нескольких часов.
Вариант A, TLS reverse proxy. Разместите Jellyfin на поддомене за Traefik с автоматическим TLS для ваших Docker-приложений или за nginx с сертификатом Let's Encrypt, выпущенным через Certbot. Jellyfin использует WebSockets для обновлений в реальном времени, поэтому прокси должен пересылать заголовки обновления (upgrade headers). Traefik делает это автоматически; для nginx их нужно прописать явно, а также использовать HTTP/1.1 при обращении к upstream, иначе обновление соединения не произойдет:
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://, чтобы локальное автообнаружение транслировало правильный URL, удаленные приложения использовали указанный вами адрес, и добавьте fail2ban для замедления попыток подбора пароля к форме входа. Как только сервер станет доступен извне, настройте Uptime Kuma на этот URL, чтобы узнавать о простоях раньше ваших пользователей. Настройте уведомления через самохостируемый сервер ntfy, тогда информация о сбое придет в виде push-уведомления на телефон, а не в письме, которое вы прочтете только на следующее утро. Вывод сервиса с авторизацией в публичный интернет — подходящий момент для аудита остальной части системы, чтобы не полагаться только на то, что отвечает лишь порт 443: sudo ss -tlnp выводит список всех открытых портов и соответствующих им процессов. Если вы также размещаете на сервере собственный код, open-kritt сканирует репозитории и их зависимости на наличие уязвимостей; он проверяет исходный код, а не открытые порты.
Вариант B, ограничение доступа через VPN. Не публикуйте порт 8096 в публичный интернет вовсе; обеспечьте доступ к Jellyfin только через туннель WireGuard, терминируемый на том же сервере. Для домашнего использования это самый простой и безопасный вариант: нет необходимости в сертификатах, нет публичного доступа, нет поверхности для брутфорс-атак. Привязка к localhost в файле compose сама по себе здесь не поможет, так как порт, опубликованный на 127.0.0.1, доступен только с самого VPS, а не с узлов VPN. Опубликуйте его на адресе туннеля сервера (например, "10.8.0.1:8096:8096", используя IP вашего сервера WireGuard) или оставьте привязку к localhost и поставьте перед ним прокси, слушающий на адресе туннеля, а затем подключайтесь через VPN. См. настройку WireGuard VPN для частного VPS для создания самого туннеля.
Планирование объема хранилища и резервное копирование
Рассчитывайте бюджет исходя из качества, а не количества файлов. Сжатые фильмы в 1080p занимают 4-15 ГБ, ремуксы 1080p — 20-40 ГБ, сезон сериала в 1080p — 15-40 ГБ, любой контент в 4K — 40-100 ГБ на фильм. Для библиотеки из нескольких сотен фильмов и сериалов требуется том объемом 2-4 ТБ. Дешевле сразу выделить избыточный объем блочного устройства, чем выполнять миграцию данных позже.
/config содержит всё состояние сервера, поэтому это единственный объект, который необходимо копировать. Создайте снапшот или остановите контейнер и заархивируйте директорию, после чего сохраните копию вне сервера:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache и папку транскодирования можно удалить без последствий. Медиафайлы на /mnt/media копируются отдельно или рассматриваются как данные, которые можно восстановить повторно; большинство пользователей выбирают второй вариант из-за большого объема. Обновления выполняются через docker compose pull && docker compose up -d; тег :12, указанный выше, остается в рамках ветки 12.x, поэтому переход на следующую мажорную версию требует осознанного изменения тега. Перед этим ознакомьтесь с примечаниями к релизу Jellyfin, так как в мажорных версиях происходят миграции схемы базы данных. Начиная с версии 12.0 (сентябрь 2026 года), Jellyfin убрал префикс "10." из номеров версий: 12.0 соответствует тому, что было бы 10.12, версии 11.x не существует, а 10.11.x была последней веткой в старой схеме. Если в вашем старом файле compose до сих пор указан jellyfin/jellyfin:10, он зафиксирован на версии 10.11 (этот тег в последний раз обновлялся 2026-06-06 до версии 10.11.11) и никогда не загрузит 12.x. Переход на :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, затем повторите сканирование.
Воспроизведение загружает процессор и вызывает буферизацию. docker stats jellyfin показывает загрузку CPU, близкую к 100% на каждое ядро, а в Dashboard → Playback сессия помечена как Transcode со скоростью ниже 1.0x. Клиент не использует прямое воспроизведение (direct play), поэтому VPS выполняет программное транскодирование медленнее реального времени и не справляется. Причина: неподдерживаемый кодек или контейнер, «вшивание» субтитров (burn-in) или тональная компрессия HDR. Решение: используйте клиент с поддержкой прямого воспроизведения, храните исходные файлы в формате H.264/AAC, используйте текстовые субтитры (SRT) вместо графических (PGS/VOBSUB), которые большинство клиентов Jellyfin не могут отрисовать самостоятельно и принудительно запускают процесс «вшивания», а также полностью исключите 4K HDR на серверах без аппаратного ускорения.
«No compatible streams are available». Полное сообщение обычно выглядит так: «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 — это самый простой и безопасный выбор для дома. Указывайте приложениям публичный адрес напрямую, так как автообнаружение работает через широковещательные рассылки в локальной сети и не доходит до клиентов, подключающихся через интернет.
Сколько дискового пространства и пропускной способности нужно для VPS с Jellyfin?
Объем диска зависит от качества: закладывайте 4-15 ГБ на сжатый фильм в 1080p, 20-40 ГБ на ремукс и 40-100 ГБ на 4K, поэтому для большинства библиотек потребуется блочное хранилище на 2-4 ТБ. Пропускная способность определяется битрейтом прямого воспроизведения: 8-12 Мбит/с на один поток 1080p и значительно больше для 4K. Убедитесь, что скорость вашего порта справляется с количеством одновременных зрителей, и следите за ежемесячным лимитом трафика. Добавьте запас по CPU, если планируете транскодирование; если планируете прямое воспроизведение, отдайте приоритет пропускной способности, а не количеству ядер.
Законно ли запускать Jellyfin на VPS?
Jellyfin — это бесплатное программное обеспечение с открытым исходным кодом, и его использование полностью законно. Важен контент: транслируйте только те медиафайлы, которыми вы владеете или на которые имеете лицензию: собственные рипы с дисков, записи или файлы, на которые у вас есть права. Jellyfin не поставляется с медиаконтентом и не предоставляет способов его получения; это плеер для библиотеки, которая уже принадлежит вам.