# Jellyfin in docker
###### guide-by-example


1. [Purpose & Overview](#Purpose--Overview)
2. [Video - core concepts](#Video---core-concepts)
3. [Jellyfin transcoding](#Jellyfin-transcoding)
4. [Clients and Plugins](#Clients-and-Plugins)
5. [Docker deploy](#Docker-deploy)
6. [Specific setups](#Specific-setups)
7. [Troubleshooting](#Troubleshooting)
# Purpose & Overview
Stream movies, shows, music to a phone, a TV, a browser, ...
Something like your own Netflix.
* [The Official site](https://jellyfin.org/)
* [Github](https://github.com/jellyfin/jellyfin)
* [DockerHub](https://hub.docker.com/r/jellyfin/jellyfin/)
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](https://jellyfin.org/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.
* `standard` vs `codec`
* **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 nvidia `NVENC` or `intel 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.
---
---
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.](https://i.imgur.com/7hSf4e1.png)
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](https://i.imgur.com/rjNFN1J.png)
on the CPU.
### Hardware acceleration
* [official docs](https://jellyfin.org/docs/general/administration/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](https://youtu.be/H0pCpNT4b-Q),
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](https://i.imgur.com/nP71y0E.png),
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](https://i.imgur.com/uxqzf6Q.png) idle
* [10x](https://i.imgur.com/NAyfXmG.png) FHD streams
* intel **n200** miniPC MSI Cubi - [5W](https://i.imgur.com/DBIGtIG.png) idle
* [10x](https://i.imgur.com/nP71y0E.png) FHD streams
* ryzen **8600G** - [14W](https://i.imgur.com/xtVm6nA.png) idle
* [10x](https://i.imgur.com/9R60Spw.png) FHD streams,
[1 stream 288 fps](https://i.imgur.com/27sSJXV.png)
* [3x](https://i.imgur.com/CkT7Df6.jpeg) 4K HDR + tonemapping,
[1 stream 86 fps](https://i.imgur.com/9AP80MO.png)
* AV1 encoding - [worked](https://i.imgur.com/ZaQRIAc.png)
* intel **i5-12600k** - [14W](https://i.imgur.com/TIvozb9.png) idle
* [10x](https://i.imgur.com/BGXOeGS.png) FHD streams
* [4x](https://i.imgur.com/yvP72UU.jpeg) 4K HDR + tonemapping
* ryzen **4350GE** miniPC ThinkCentre M75q Gen2 - [4W](https://i.imgur.com/S7NL4GD.png) idle
* 6x FHD streams
* 1x 4K HDR + tonemapping - [1x stream 33 fps](https://i.imgur.com/8iairvO.png)
* ryzen **5500GT** - [16W](https://i.imgur.com/UANzgLd.png) idle
* 6x FHD streams,
[1 stream 185 fps](https://i.imgur.com/O5BfVZB.png)
* 1x 4K HDR + tonemapping - [1x stream 46 fps](https://i.imgur.com/wsrUoPe.png)
* ryzen **7600X** - [23W](https://i.imgur.com/uCFJz4S.jpeg) idle
* 10x FHD streams
* ryzen **9600X** - [18W](https://i.imgur.com/OQoAOwr.jpeg) idle
* 5x FHD streams,
[1 stream 148 fps](https://i.imgur.com/O3P66zX.png)
* 1x 4K HDR + tonemapping,
[1 stream 32 fps](https://i.imgur.com/347N12D.png)
* ultra7 **265k** - [19W](https://i.imgur.com/DnIRBO1.jpeg) idle
* 1 stream [633 fps](https://i.imgur.com/eXUldZm.png)
* 1 stream 4K HDR + tonemapping [200 fps](https://i.imgur.com/ktr6erU.png)
* ryzen **8700GE** miniPC(90W) - [8W](https://i.imgur.com/5hZLzqb.jpeg) idle
* 1 stream [270 fps](https://i.imgur.com/C4WehOt.png), AV1 [380 fps](https://i.imgur.com/LEwPrY1.png)
* 1 stream 4K HDR + tonemapping [60 fps](https://i.imgur.com/1YjpnwV.png), AV1 [70 fps](https://i.imgur.com/8htVRzB.png)
Software used for monitoring the cpu and gpu usage
* [btop](https://github.com/aristocratos/btop)
* `intel_gpu_top` from [intel-gpu-tools](https://archlinux.org/packages/extra/x86_64/intel-gpu-tools/) package
* `radeontop` from [radeontop](https://archlinux.org/packages/extra/x86_64/radeontop/) package
and `rocm-smi-lib` to show in btop gpu load
---
---
# Clients and Plugins
Jellyfin's open source nature allows developers to create unofficial client apps.
* [Awesome Jellyfin Clients List](https://github.com/awesome-jellyfin/awesome-jellyfin/blob/main/CLIENTS.md)
The ones I tried and find interesting.
#### [Findroid](https://github.com/jarnedemeulemeester/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](https://github.com/jmshrv/finamp)
Aimed for music. Did not tested much yet.
### Plugins
* [intro-skipper](https://github.com/intro-skipper/intro-skipper?tab=readme-ov-file)
\- 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 share
* `jellyfin_cache/` - cache, includes transcodes
* `jellyfin_config/` - configuration
* `.env` - a file containing environment variables for docker compose
* `compose.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
* [the official documentation](https://jellyfin.org/docs/general/installation/container)
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`
```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`
```bash
# 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](https://github.com/DoTheEvo/selfhosted-apps-docker/tree/master/caddy_v2).
`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](https://forum.manjaro.org/t/root-tip-systemd-mount-unit-samples/1191),
instead of fstab or autofs.
`/etc/systemd/system/mnt-bigdisk.mount`
```ini
[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`
```ini
[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
* [the official documentation](https://jellyfin.org/docs/general/administration/hardware-acceleration/intel/#configure-with-linux-virtualization)
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/renderD128`
* `Enable hardware decoding for`
* Check everything **except** `AV1` and `HEVC RExt 12bit`
* if 12th gen+ check everything
* `Enable hardware encoding` - check
* `Enable VPP Tone mapping` - check
* `Enable Tone mapping` - check
* `Encoding preset` - `Auto`
* `Throttle Transcodes` - check
* `Delete 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
* [the official documentation](https://jellyfin.org/docs/general/administration/hardware-acceleration/amd#configure-with-linux-virtualization)
* [some videos](https://youtu.be/H0pCpNT4b-Q) and [some takes](https://youtu.be/UNJLDS5gC7o)
on [AMD transcoding](https://youtu.be/pnvp9DtqVjo)
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/renderD128`
* `Enable hardware decoding for`
* Check everything **except** `AV1` and `HEVC RExt 12bit`
* if 7000+ check everything
* `Enable hardware encoding` - check
* `Allow encoding in AV1 format` - check if 8000G+
* `Enable Tone mapping` - check, but needs work
* `Encoding preset` - `Auto`
* `Throttle Transcodes` - check
* `Delete 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**
[default](https://i.imgur.com/4EIv4mo.png) | [after](https://i.imgur.com/rbvsJQ9.png)
---
---
# 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 pull`
- `docker compose up -d`
- `docker image prune`
# Useful
* [Jellyfin on ARM - orange pi 5](https://akashrajpurohit.com/blog/setup-jellyfin-with-hardware-acceleration-on-orange-pi-5-rockchip-rk3558/)
* https://www.reddit.com/r/selfhosted/comments/1bit5xr/livetv_on_jellyfin_2024/