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

# Purpose & Overview
Stream movies/tv-shows/music to a browser, or a [large selection of devices and services.](https://jellyfin.org/clients/)
* [Official site](https://jellyfin.org/)
* [Github](https://github.com/jellyfin/jellyfin)
* [DockerHub](https://hub.docker.com/r/jellyfin/jellyfin/)
Jellyfin if a free media system, an alternative to proprietary Plex.
The core server side is written in C#, web client in Javascript,
and a number of other clients written in various languages and frameworks.
Starting point for me was [this viggy96 repo](https://github.com/viggy96/container_config)
# 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
A relatively simple compose.
The only atypical thing is the **passthrough** of the graphic card
for hardware accelerated transcoding.
In the `devices` section a passthrough of a graphic card is done,
`/dev/dri/renderD128` refering to the first gpu of the system
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_config:/config
- ./jellyfin_cache:/cache
- /mnt/bigdisk/serialy:/media/tv:ro
- /mnt/bigdisk/filmy_2:/media/movies:ro
ports:
- "8096:8096" # webGUI
- "1900:1900/udp" # autodiscovery on local networks
networks:
default:
name: $DOCKER_MY_NETWORK
external: true
```
`.env`
```bash
# GENERAL
DOCKER_MY_NETWORK=caddy_net
TZ=Europe/Bratislava
```
**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
}
```
# The first run
Click through basic setup.
WORK IN PROGRESS
WORK IN PROGRESS
WORK IN PROGRESS
# Transcoding
### The basics
* a **video file** is just a bunch of pictures - **frames**,
somehow packed in to one file.
* To save up disk space and bandwidth its **compressed** using some video
standard/codec.
* MPEG-2 - stuff of the past
* **H.264** - the most common now
* **H.265** - also called **HEVC**, fast spreading, 50% improved over H.264
* **AV1** - the future, open codec - no licencing fees, more improvements
* Ways to transcode
* **Software** - cpu does the job, uses some library, it is **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 - a tiny part of a cpu/gpu/soc
that is designed for just one thing - to transcode a specific video standard.
That means it is **extremely efficient** at it.
* Terminology
* **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
Ideally you deploy jellyfin somewhere with an igpu to get hardware accelerated
transcoding, but it is far from required.
For most people, majority of media will be in H.264 or H.265 which will be
**direct play** - no transcoding required on most devices.
Even if theres occasional need to transcode, average cpu can do one or two streams.
If you plan to serve more people and have larger library you should
definitly plan to have something with igpu
#### HDR
The issue starts with 4k content, of which majority also uses
HDR - High Dynamic Range. This is for benefit of HDR TVs, monitors, phones,...
To play on non-HDR devices transcoding is always required and not just typical
transcoding, but also tonemapping as trancoding HDR content without it will make colors seem
heavily desaturated - washed out.
* Not all devices like phones, PCs - browsers, TVs, streaming boxes,...
have build in support for all these standard.
* If video is in H.265 but firefox on linux cant decode it,
jellyfin detects this and transcodes it to something that can be played.
### intel igpu
to get "Enable Intel Low-Power H.264 hardware encoder" working
* `echo "options i915 enable_guc=2" > /etc/modprobe.d/i915.conf`
* `mkinitcpio -P`
* reboot
The above compose basic setup worked for me
* ryzen 1700, headless, without any gpu
* modern intel cpus with igpu - n200, i5-125600k
* modern amd ryzens with igpu - 7700x, 5500GT
but how to setup things might change over time so one should check
[the official documentation](https://jellyfin.org/docs/general/administration/hardware-acceleration/intel#configure-with-linux-virtualization)
# Specifics of my setup
* no real long term use
* findroid app does not jump subtitles like official one
* amd cpu and no gpu, so no experience with hw transcoding
* media files are stored and shared on trunas scale VM
and 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,file_mode=0700,dir_mode=0700,uid=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`
# Troubleshooting

*We're unable to connect to the selected server right now. Please ensure it is running and try again.*
If you encounter this, try opening the url in browsers private window.
If it 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
* https://www.reddit.com/r/selfhosted/comments/1bit5xr/livetv_on_jellyfin_2024/