Запуск Jellyfin через обратный прокси Traefik с Docker Compose

Jellyfin — свободный медиасервер без подписок и телеметрии: фильмы, сериалы, музыка и фото с телевизора, телефона или браузера. На домашнем сервере это как раз та «фильмотека», о которой шла речь во введении к заметкам — рядом с Immich для фото и Nextcloud для документов.

Нужны рабочий Traefik и сеть traefik_network. Порт 8096 наружу не открываем: доступ только через HTTPS.

1. Каталог и пути к медиа

mkdir -p jellyfin/{config,cache}
cd jellyfin

Каталоги с фильмами и сериалами уже должны лежать на дисковом массиве. В примере ниже:

  • /mnt/media/movies → /media/movies
  • /mnt/media/shows → /media/shows

Замените пути на свои.

2. docker-compose.yml

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    user: "1000:1000"
    environment:
      - TZ=Europe/Astrakhan
      - JELLYFIN_PublishedServerUrl=https://jellyfin.(yourdomain)
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /mnt/media/movies:/media/movies:ro
      - /mnt/media/shows:/media/shows:ro
    # ports:   # не публикуем — только Traefik
    #   - "8096:8096"
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.jellyfin_https.redirectscheme.scheme=https
      - traefik.http.routers.jellyfin.entrypoints=web
      - traefik.http.routers.jellyfin.middlewares=jellyfin_https@docker
      - traefik.http.routers.jellyfin.rule=Host(`jellyfin.(yourdomain)`)
      - traefik.http.routers.jellyfin_https.entrypoints=websecure
      - traefik.http.routers.jellyfin_https.rule=Host(`jellyfin.(yourdomain)`)
      - traefik.http.routers.jellyfin_https.tls=true
      - traefik.http.routers.jellyfin_https.tls.certresolver=le
      - traefik.http.services.jellyfin.loadbalancer.server.port=8096
    networks:
      - traefik_network

networks:
  traefik_network:
    external: true

⚠️ Замените (yourdomain) на ваш домен. user: "1000:1000" — UID/GID владельца медиа на хосте (id -u / id -g); иначе контейнер не прочитает библиотеку.

На AlmaLinux / Rocky Linux 9 при SELinux в режиме enforcing к bind-mount добавьте :Z (или :z, если каталог делят несколько контейнеров), как в заметке про SELinux и Docker volumes:

      - ./config:/config:Z
      - ./cache:/cache:Z
      - /mnt/media/movies:/media/movies:ro,z
      - /mnt/media/shows:/media/shows:ro,z

3. Запуск

docker compose up -d

Откройте https://jellyfin.(yourdomain), создайте администратора и добавьте библиотеки с путями /media/movies и /media/shows.

4. Known Proxies (обязательно за Traefik)

Без этого Jellyfin игнорирует X-Forwarded-* и в логах/банах видит IP прокси, а не клиента.

Dashboard → Networking → Known Proxies — укажите подсеть Docker-сети Traefik (часто что-то вроде 172.16.0.0/12 или конкретный CIDR traefik_network):

docker network inspect traefik_network --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'

Сохраните настройки и перезапустите контейнер при необходимости:

docker compose restart jellyfin

5. Аппаратный транскодинг (по желанию)

Если на хосте есть GPU / iGPU и устройство /dev/dri, добавьте в сервис:

    group_add:
      - "992"   # GID группы render: getent group render
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/dri/card0:/dev/dri/card0

GID render смотрите командой getent group render — на разных дистрибутивах он разный (часто 992, 107 или 122).

После запуска: Dashboard → Playback → Transcoding — включите VAAPI / QSV / NVENC под ваше железо. На VPS без GPU этот блок не нужен: оставайтесь на Direct Play / софт-транскодинге.

Для NVIDIA вместо /dev/dri обычно нужен nvidia-container-toolkit и блок deploy.resources.reservations.devices, как в заметке про Open WebUI и Ollama.

6. Кастомизация и кодеки

Jellyfin легко подстроить под себя: оформление, веб-клиент и набор форматов, которые сервер умеет отдавать без лишней потери качества.

Внешний вид и веб-клиент

  • Dashboard → Branding / Custom CSS — цвета, шрифты, логотип, фон; CSS можно держать в файле рядом с compose и вставлять в панель.
  • Файлы веб-клиента из образа можно перекрыть bind-mount’ами (index.html, чанки .js, иконки) — так добавляют свои скрипты в шапку, визуализатор «сейчас играет», бейджи форматов на альбомах и треках.
  • После смены CSS/JS в браузере сделайте жёсткий сброс кэша (иначе увидите старую сборку).

Кодеки и качество звука / видео

В образе уже есть FFmpeg: сервер умеет и Direct Play, и транскодинг. Имеет смысл явно настроить воспроизведение под вашу библиотеку:

  • Храните музыку в FLAC / ALAC / AAC / mp3 (и другие контейнеры, которые есть в библиотеке) — Jellyfin подхватит их при сканировании.
  • Dashboard → Playback — разрешите нужные кодеки, выберите качество транскодинга аудио/видео; для «прозрачного» звука по возможности используйте Direct Play / remux без перекодирования.
  • На клиентах с кривой поддержкой lossless (например Safari и вшитая обложка в файле) сервер может перекодировать в AAC — это настраивается политикой воспроизведения / правкой клиента, если нужен стабильный звук в браузере.
  • Для редких контейнеров и дорожек (TrueHD, DTS, HEVC 10-bit и т.п.) опирайтесь на FFmpeg в контейнере и при необходимости на аппаратный декодер из §5: иначе клиент получит совместимый поток, а не «тишину».

Так медиатека не ограничена «удобными для стриминга» форматами: можно держать исходники в лучшем качестве и отдавать клиенту либо оригинал, либо аккуратный транскод.

7. Практические замечания

  • Медиа монтируйте :ro, если Jellyfin не должен менять файлы; запись понадобится только для загрузки или правок метаданных на диске.
  • Бэкапьте ./config (пользователи, библиотеки, метаданные). ./cache можно не бэкапить — пересоберётся.
  • Клиенты: веб, приложения Jellyfin / Infuse / Findroid; для автообнаружения в LAN иногда нужен UDP 7359 и network_mode: host — за Traefik с публичным HTTPS это обычно не требуется.
  • Не открывайте 8096 в NAT роутера: достаточно 80/443 на Traefik.

Итог: своя фильмотека за HTTPS на том же стеке Docker + Traefik, с кастомизацией интерфейса и кодеками под реальные форматы библиотеки.