18 KiB
Jellyfin in docker
guide-by-example
- Purpose & Overview
- Video - core concepts
- Jellyfin transcoding
- Clients and Plugins
- Docker deploy
- Specific setups
- Troubleshooting
Purpose & Overview
Stream movies, shows, music to a phone, a TV, a browser, ...
Something like your own Netflix.
Jellyfin is free and opensource without any limitation.
The server can be installed on windows, or on linux or run as a docker container.
It's written in C#, web client in Javascript
and a number of other clients written in
various languages and frameworks.
Video - core concepts
- a video file is a bunch of pictures - frames, packed in to one file.
- To save disk space and bandwidth it's compressed using a video standard/codec.
- H.262 - also called MPEG-2 - stuff of the past.
- H.264 - the most common now, also called AVC or MPEG-4.
- H.265 - also called HEVC, fast spreading, 50% improved over H.264,
but it also greatly increased fees for developers and manufacturers and came with a convoluted patent pools. - AV1 - the future, no royalty fees, more improvements. The successor to VP9, which was developed by google.
- Ways to transcode
- Software - cpu does the job, uses some library, it's very cpu heavy
a phone doing a software playback would either stutter, or be through the entire battery in 30 minutes. - Hardware - there is a dedicated hardware -
ASIC- a tiny part of a cpu/gpu/soc that is designed for just one thing - to transcode a specific video standard. It's extremely efficient at it.
- Software - cpu does the job, uses some library, it's very cpu heavy
standardvscodec- Video Compression Standard - set of specifications to follow, like a recipe, examples are H.264, H.265, AV1,..
- Codec - The actual software or hardware implementation of a standard,
a cook that follows a recipe and gives you a finished meal.
Examples are
x264,x265,libaom, or hardware ASICs on a gpu or a cpu like nvidiaNVENCorintel quick sync.
- Other terminology
- Bitrate - how much data per seconds flow, for example 2Mbps or 20Mbps,
higher bitrate means better quality and larger files/bandwidth needs - Decode - taking a compressed video file and turning it into a viewable format.
- Encode - compressing raw video in to a specific video format
- Transcode - converting a video format in to a different format, consists of both decode and encode steps
- Direct Play - No transcoding required. The device that is trying to play the video is able to do so with its hardware.
- HDR - High Dynamic Range. A new technology to improve contrast and visible details but lacks wide hardware support yet. The opposite is SDR - Standard Dynamic Range.
- ffmpeg - a command line application, open source, contains codec libraries to be a single tool for all video manipulation. Is often the one under the hood of any media related software, doing the work.
- Bitrate - how much data per seconds flow, for example 2Mbps or 20Mbps,
Jellyfin transcoding
Jellyfin streams media files to client applications.
In most cases it's direct play, no transcoding required and any machine
can do dozens of concurent streams in direct play.
But there are cases when a client does not support codec of the video file.
Example is Firefox playing a movie encoded in HEVC(x265).
Jellyfin knows which codecs client supports and if needed
it transcodes video on the fly, as it's being streamed.
Another reason to transcode can be if you set a hard limit on bandwidth
use per stream.
In Dashboard > Playback > Streaming setting lets say
2Mbps.
Out of the box, jellyfin is set to use cpu for software transcoding. It will work fine for one or two simple 1080p concurent streams, unless the cpu is really underpowered/old, but it will put a heavy load on the CPU.
Hardware acceleration
Ideally jellyfin is running on something that can offer hardware accelerated transcoding, using iGPU which greatly improves performance and power consumption.
- Intel - QSV - intel quicksync. Go-To recommendation and ideal for most users, high quality, fast, reliable drivers, cheap and low power consumption as an igpu. Huge userbase.
- AMD - VAAPI when in docker - kinda shunned as historicly it had the worst
encode quality. AMD improved some,
but so did the competition so it's still not as good. But in real use
most people would likely not be able to tell difference.
AV1 encode supported by their newest stuff finally concludes the discussion as it's praised for great quality. - nvidia - NVENC - quality not as good as intel, but better than amd, driver limit of 8 encoding sessions. Not an ideal solution as a pcie card will consume some extra power.
- Intel Arc - QSV - relatively new hardware, should be great, but be careful about idle power consumption, some reviews post ~30W idle for arc while ~5W for amd/nvidia.
But to repeat:
Very likely majority of media will be in h.264 and h.265 and overwhelming
majority of devices can play these straight up - direct play. So no need to
overthink transcoding hardware much.
That is unless you want to go 4K + HDR.
4K and HDR
With 4K resolution often comes HDR - High Dynamic Range.
If it's played on HDR device all is fine and theres better contrast and details.
But non-HDR devices need transcoding with tonemapping.
Tonemapping is what converts HDR content to SDR - Standard Dynamic Range.
Without tonemapping the colors would be heavily desaturated - washed out.
Some clients, like Findroid on android use mpv for playback, which is able to do just tonemapping without the whole transcoding so it is almost as direct play in terms of load.
Testing various hardware
Archlinux straight on metal, Jellyfin as a docker container.
Typical test is running 10x x265 FHD streams,
and 4x x265 4K HDR + tonemapping till see stutter.
The early tests were without 4K. The newer test is just one FHD and one 4k stream and seeing the raw FPS. You can divide that number by 30 to estimate how many concurrent streams the igpu can handle.
BTW, power consumption can vary somewhat depending on motherboard, network card, OS, C-states, ... these numbers are just for rough idea.
- ryzen 7700X - 22.4W idle
- 10x FHD streams
- intel n200 miniPC MSI Cubi - 5W idle
- 10x FHD streams
- ryzen 8600G - 14W idle
- 10x FHD streams, 1 stream 288 fps
- 3x 4K HDR + tonemapping, 1 stream 86 fps
- AV1 encoding - worked
- intel i5-12600k - 14W idle
- ryzen 4350GE miniPC ThinkCentre M75q Gen2 - 4W idle
- 6x FHD streams
- 1x 4K HDR + tonemapping - 1x stream 33 fps
- ryzen 5500GT - 16W idle
- 6x FHD streams, 1 stream 185 fps
- 1x 4K HDR + tonemapping - 1x stream 46 fps
- ryzen 7600X - 23W idle
- 10x FHD streams
- ryzen 9600X - 18W idle
- 5x FHD streams, 1 stream 148 fps
- 1x 4K HDR + tonemapping, 1 stream 32 fps
- ultra7 265k - 19W idle
- ryzen 8700GE miniPC - 8W idle
Software used for monitoring the cpu and gpu usage
- btop
intel_gpu_topfrom intel-gpu-tools packageradeontopfrom radeontop package androcm-smi-libto show in btop gpu load
Clients and Plugins
Jellyfin's open source nature allows developers to create unofficial client apps.
The ones I tried and find interesting.
Findroid
Feels bit better in user interface and control.
Does not jump subtitles like the official client,
also seems it's always directplay even 4k+HDR, have to investigate
how the fuck does it do. Uses mpv, but does it mean theres some big load
on a phone when compared to the official app?
It ignores max streaming bandwidth limit when server streams over the internet.
Finamp
Aimed for music. Did not tested much yet.
Plugins
- intro-skipper - skips intros for shows
Docker deploy
Files and directory structure
/mnt/
└── bigdisk/
├── tv/
├── movies/
└── music/
/home/
└── ~/
└── docker/
└── jellyfin/
├── jellyfin_cache/
├── jellyfin_config/
├── .env
└── compose.yml
/mnt/bigdisk/...- a mounted media storage sharejellyfin_cache/- cache, includes transcodesjellyfin_config/- configuration.env- a file containing environment variables for docker composecompose.yml- a docker compose file, telling docker how to run the containers
You only need to provide the two files.
The directories are created by docker compose on the first run.
Compose
A relatively simple compose.
The only atypical thing is the passthrough of the graphic card
for hardware accelerated transcoding.
It's in the devices section /dev/dri/renderD128
In group_add section permissions are set.
You want to execute the command: getent group render | cut -d: -f3
to get the correct group number for you system and set it in.
This all can be left as is even if no gpu is planned to be used.
compose.yml
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
hostname: jellyfin
restart: unless-stopped
env_file: .env
devices:
- /dev/dri/renderD128:/dev/dri/renderD128 # ls /dev/dri
group_add:
- "989" # match output of: getent group render | cut -d: -f3
volumes:
- ./jellyfin_cache:/cache
- ./jellyfin_config:/config
- /mnt/bigdisk/serialy:/media/shows:ro
- /mnt/bigdisk/filmy_2:/media/movies:ro
ports:
- "8096:8096" # webGUI
- "1900:1900/udp" # autodiscovery on local networks
- "7359:7359/udp" # autodiscovery on local networks
networks:
default:
name: $DOCKER_MY_NETWORK
external: true
.env
# GENERAL
DOCKER_MY_NETWORK=caddy_net
TZ=Europe/Bratislava
# JELLYFIN
# url or ip address
JELLYFIN_PublishedServerUrl=https://tv.example.com
All containers must be on the same network.
Which is named in the .env file.
If one does not exist yet: docker network create caddy_net
Reverse proxy
Caddy is used, details
here.
Caddyfile
tv.{$MY_DOMAIN} {
reverse_proxy jellyfin:8096
}
Mounting network shares
If the media files are stored on an SMB share and should be mounted directly on the docker host using systemd mounts, instead of fstab or autofs.
/etc/systemd/system/mnt-bigdisk.mount
[Unit]
Description=12TB truenas mount
[Mount]
What=//10.0.19.11/Dataset-01
Where=/mnt/bigdisk
Type=cifs
Options=ro,username=ja,password=qq,vers=3.0,soft,file_mode=0644,dir_mode=0755,uid=1000,gid=1000
DirectoryMode=0700
[Install]
WantedBy=multi-user.target
/etc/systemd/system/mnt-bigdisk.automount
[Unit]
Description=12TB truenas mount
[Automount]
Where=/mnt/bigdisk
[Install]
WantedBy=multi-user.target
to automount on boot - sudo systemctl enable mnt-bigdisk.automount
The first run
Click through the basic setup.
- Change the server's name
Dashboard>General>Server name
Specific setups
Intel specific setup
Assuming an intel cpu with an igpu of the last ~8 years.
The compose.yml + .env should just work.
Hardware Accelerated Transcoding.
Dashboard > Playback > Transcoding
Hardware acceleration- Intel QuickSync (QSV)QSV Device-/dev/dri/renderD128Enable hardware decoding for- Check everything except
AV1andHEVC RExt 12bit - if 12th gen+ check everything
- Check everything except
Enable hardware encoding- checkEnable VPP Tone mapping- checkEnable Tone mapping- checkEncoding preset-AutoThrottle Transcodes- checkDelete segments- check
To be able to use Enable Intel Low-Power H.264 hardware encoder
- On the docker host
echo "options i915 enable_guc=2" > /etc/modprobe.d/i915.conf mkinitcpio -P- reboot
AMD specific setup
Assuming an amd cpu with vega or RDNA.
The compose.yml + .env should just work.
Dashboard > Playback > Transcoding
Hardware acceleration- Video Acceleration API (VAAPI)QSV Device-/dev/dri/renderD128Enable hardware decoding for- Check everything except
AV1andHEVC RExt 12bit - if 7000+ check everything
- Check everything except
Enable hardware encoding- checkAllow encoding in AV1 format- check if 8000G+
Enable Tone mapping- check, but needs workEncoding preset-AutoThrottle Transcodes- checkDelete segments- check
Tone-mapping
- for my arch host installing these packages
sudo pacman -S xf86-video-amdgpu libva-mesa-driver mesa-vdpau vulkan-radeon vulkan-tools - reboot
Screenshots
Troubleshooting
Playback failed due to a fatal player error
- docker host - go to
jellyfin/jellyfin_config/log/ - the last file in the folder is the last playback attempt
- paste that in to chatgpt
But generally, if it's after you made some transcoding options changes, go back to default, see if that works and then only enable stuff one by one.
Autodiscovery not working
to check if autodiscovery port is open and server is responding:
echo -n 'Who is JellyfinServer?' | nc -u -b 10.0.19.4 7359
Unable to connect to the selected server right now
We're unable to connect to the selected server right now. Please ensure it's running and try again.
- try opening the url in browsers private window
- if that works then clear the cookies in your browser
No playback at all but GUI works fine
Might be no access to network share, for example if dockerhost boots up faster than NAS.
Update
Manual image update:
docker compose pulldocker compose up -ddocker image prune





