Установка Jellyfin на VPS через Docker
Настройте Jellyfin в Docker на VPS. Узнайте, как избежать ошибок прав доступа к volume и почему транскодирование на CPU может перегрузить ваш сервер.
Что вы создаете
Медиасервер Jellyfin на VPS: один контейнер, три volume и блочный диск для хранения фильмов и сериалов. Сервер будет доступен через любой браузер или приложение Jellyfin. Установка выполняется с помощью compose-файла из пятнадцати строк. Большинство последующих проблем возникают по двум причинам: права доступа к файлам, которые контейнер не может прочитать, и попытки запустить транскодирование видео на VPS без GPU. Данное руководство посвящено именно этим двум аспектам, так как именно они чаще всего вызывают обращения в службу поддержки.
Jellyfin — это бесплатное ПО с открытым исходным кодом. В нем нет учетных записей, платных функций и телеметрии. Это причина, по которой проект попадает почти в каждый список вещей, достойных self-hosting в 2026 году. Программа воспроизводит медиафайлы, которыми вы владеете. Она не предоставляет контент, и данное руководство не предназначено для его поиска.
Реальность транскодирования перед арендой сервера
Прочитайте это в первую очередь, так как это влияет на выбор оборудования. При нажатии кнопки воспроизведения медиасервер выполняет одно из двух действий. Direct play передает файл без изменений: VPS считывает байты с диска и отправляет их по сети, что практически не нагружает CPU. Transcoding выполняет перекодирование видео на лету — изменение разрешения, кодека или вшивание субтитров — и это требует значительных ресурсов 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; remux 1080p Blu-ray — 20–30 Mbps; 4K HDR — 40–80 Mbps. Если три человека одновременно смотрят файлы с битрейтом 10 Mbps, ваш VPS должен обеспечивать постоянную исходящую скорость 30 Mbps. Проверьте два параметра вашего тарифного плана: скорость порта (может ли он обеспечивать 30 Mbps на upload?) и месячный лимит трафика. Один двухчасовой фильм с битрейтом 10 Mbps потребляет около 9 GB. Таким образом, лимит в 1 TB/month позволит просмотреть чуть более ста таких фильмов в месяц (по три-четыре в день), а просмотр 4K-контента (битрейт которого в 4–8 раз выше) израсходует лимит гораздо быстрее.
Предварительные требования
- Чистая Ubuntu 24.04 KVM VPS с правами 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/mediaВажно учитывать nofail: без этой настройки, если блочный том будет отсоединен, система не загрузится и перейдет в режим аварийной оболочки (emergency shell). Самая частая ошибка — запуск команды 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/jellyfin не поддерживает переменные окружения PUID/PGID — они относятся к образу от LinuxServer.io (lscr.io/linuxserver/jellyfin). В официальном образе пользователь управляется через ключ user: в compose; если этот ключ не указан, контейнер запускается от имени root. В обоих случаях правило одинаково: uid/gid, под которым запускается контейнер, должен иметь права на чтение и выполнение (traversal) для каждой директории с медиафайлами.
Мы будем использовать 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), а не только бит read. Без него контейнер не сможет войти в папку, даже если он может просмотреть список имен файлов. Ошибка, приводящая к пустому списку всей библиотеки, часто связана с родительской директорией: если uid контейнера не может войти в саму точку монтирования, он не сможет получить доступ к /media/Movies или /media/Shows. В этом случае в логах появится ошибка Access to the path ... is denied, и все библиотеки станут пустыми. Любая папка с медиафайлами, которую контейнер не сможет прочитать, будет пропущена, и запись об этом появится в логах. Таким образом, пакет файлов, скопированных под root, бесследно исчезнет из библиотеки. Именно поэтому необходимо выполнять chown рекурсивно и устанавливать бит 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. Мастер предложит выбрать язык, а затем создать пользователя admin с надежным паролем. Этот аккаунт является основным для сервера, поэтому не используйте временные пароли. Добавьте вашу первую библиотеку: выберите тип контента Movies, укажите путь /media/Movies (путь внутри контейнера, а не путь на хосте) и повторите процедуру для Shows по пути /media/Shows. Завершите настройку, после чего Jellyfin начнет сканирование. При небольшом объеме библиотеки правильный результат — появление постеров и названий в течение одной-двух минут. Добавлять или редактировать библиотеки можно позже в разделе Dashboard → Libraries; для принудительного обновления используйте Scan All Libraries.
Если вы планируете использовать транскодирование, откройте Dashboard → Playback → Transcoding и установите путь для временных файлов транскодирования на /cache/transcodes. Это позволит избежать переполнения тома /config за счет использования кэш-тома. Установите параметр аппаратного ускорения в значение None, так как в системе отсутствует GPU для ускорения.
Удаленный доступ: TLS reverse proxy или использование VPN
Существует два безопасных способа доступа к Jellyfin из внешней сети и один небезопасный, которого следует избегать. Небезопасный способ — это прямой проброс порта 8096 в интернет: учетные данные передаются в открытом виде, а порт подвергается перебору паролей (brute-force) в течение нескольких часов.
Вариант А — TLS reverse proxy. Разместите Jellyfin на поддомене за Traefik с автоматическим TLS для Docker-приложений или за nginx с сертификатом Let's Encrypt, выданным через Certbot. Jellyfin использует WebSockets для обновлений в реальном времени, поэтому прокси-сервер должен передавать заголовки upgrade. 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://, чтобы локальное автообнаружение (autodiscovery) передавало правильный URL — удаленные приложения используют указанный вами адрес. Также установите fail2ban для защиты от перебора паролей для учетной записи пользователя. После того как сервер станет публичным, настройте Uptime Kuma на этот URL, чтобы узнать о сбое раньше ваших зрителей.
Вариант Б — использование VPN для приватного доступа. Не публикуйте порт 8096 в сети. Доступ к Jellyfin должен осуществляться только через туннель WireGuard, завершающийся на этом же узле. Для домашнего использования это самый простой и безопасный вариант: не требуются сертификаты, нет публичного доступа и нет векторов для атак перебором. Привяжите контейнер к адресу туннеля или к 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. Дешевле сразу выделить избыточный объем блочного хранилища, чем выполнять миграцию данных позже.
/config содержит все состояние сервера, поэтому это единственный объект, который необходимо архивировать. Сделайте snapshot или выполните команду stop-and-tar и храните копию на другом устройстве:
docker compose down
sudo tar czf jellyfin-config-$(date +%F).tgz -C ~/jellyfin config
docker compose up -d/cache и папка transcode не являются критичными. Медиафайлы на /mnt/media необходимо архивировать отдельно или принять риск их потери — из-за объема данных большинство пользователей выбирают второй вариант. Обновления docker compose pull && docker compose up -d; тег :10 остается в рамках мажорной версии 10.x, поэтому переход на следующую мажорную версию требует ручного редактирования тега. Перед обновлением изучите примечания к релизу Jellyfin, так как миграция схемы библиотеки происходит при переходе на новые мажорные версии.
Режимы сбоев и соответствующие сообщения
Библиотека пуста после сканирования. В логах по адресу Dashboard → Logs (или ~/jellyfin/config/log/log_*.log) отображается:
System.UnauthorizedAccessException: Access to the path '/media/Movies' is denied.UID контейнера не имеет прав на чтение этого пути. Причина: медиафайлы принадлежат root или другому UID, отличному от вашего значения user:; у директории отсутствует бит исполнения (execute bit); или родительская точка монтирования недоступна для данного UID. Решение: chown -R 1000:1000 /mnt/media, директории 755, файлы 644, затем повторное сканирование.
Воспроизведение загружает CPU и буферизуется. docker stats jellyfin показывает загрузку CPU, близкую к 100% на каждое ядро, а в Dashboard → Playback сессия указана как Transcode со скоростью ниже 1.0x. Клиент не использует direct-play, поэтому VPS выполняет транскодирование медленнее реального времени, что приводит к потере данных. Причина: неподдерживаемый кодек или контейнер, вшивание субтитров (burn-in) или HDR tone-mapping. Решение: используйте клиент с поддержкой direct-play, храните исходники в H.264/AAC, используйте текстовые субтитры (SRT) вместо графических (PGS/VOBSUB), которые требуют вшивания, и не используйте 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, проверьте возможность воспроизведения файла, проверьте права доступа пользователя к транскодированию и попробуйте другой клиент, чтобы исключить особенности кодеков в браузере.
У фильмов отсутствует постер или выбран неверный. Метаданные не совпали. Причина: фильм находится не в своей папке 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 использует широковещательные запросы в локальной сети и не работает через интернет.
Сколько дискового пространства и трафика требуется для Jellyfin VPS?
Объем диска зависит от качества: закладывайте 4–15 GB на сжатый фильм 1080p, 20–40 GB на remux и 40–100 GB на 4K. Большинству библиотек требуется блок объемом 2–4 TB. Потребление трафика зависит от битрейта при direct-play: 8–12 Mbps для потока 1080p и значительно больше для 4K. Убедитесь, что скорость порта позволяет обслуживать нужное количество одновременных зрителей, и следите за лимитом ежемесячного трафика. Если вы планируете транскодирование, выделите больше ресурсов CPU; если планируете только direct-play, приоритет следует отдать пропускной способности канала.
Законно ли использовать Jellyfin на VPS?
Сам Jellyfin является бесплатным программным обеспечением с открытым исходным кодом, его использование полностью законно. Важно только содержимое: транслируйте только те медиафайлы, которыми вы владеете или на которые у вас есть лицензия (ваши собственные рипы с дисков, записи или файлы, на которые у вас есть права). Jellyfin не содержит медиаконтента и не предоставляет способов его получения; это плеер для вашей собственной библиотеки.