Jellyfin Mediaserver auf meinem Rock64 Mini NAS

Durch einige Posts auf Mastodon und ein Video von Jeff Geerling bin ich auf die Multimediaserver-Software Jellyfin aufmerksam geworden. Mir war das Konkurrenzrodukt Plex bereits bekannt, aber mit Jellyfin bekommt man eine reine FOSS Lösung, bei der man sich keine Gedanken um Lizensierung oder Kosten machen muss. Für meine Ansprüche - vor allem Musikstreaming - sollte Jellyfin genügen, daher habe ich es kurzerhand auf meinem Rock64-basierten Mini NAS installiert.

Setup

Jellyfin kann als Debian-Paket oder Container installiert werden. Bei den Containerumgebungen kann man aus Docker und Podman wählen. Da Jellyfin für mich neu ist und ich den Betrieb erst einmal erproben will, habe ich mich für ein Podman-basiertes Setup entschieden. Auf Docker verzichte ich gerne, wenn es geht. Das modernere Podman mit seinen Features ist mir grundsätzlich sympathischer - aber das ist eine andere Geschichte und soll hier keine weitere Rolle spielen.

Zunächst wird Podman inkl. zweier Abhängigkeiten von den Ubuntu/Armbian-Paketquellen installiert:

apt install podman uidmap slirp4netns

Danach wird ein neuer User angelegt, unter dem der Jellyfish-Container später ausgeführt werden soll. Mit Podman können Container ohne Daemon und ohne Root-Rechte als einfacher Benutzer ausgeführt werden. Von diesem Sicherheitsvorteil will ich selbstverständlich profitieren:

adduser --disabled-password --home /opt/jellyfin jellyfin
su - jellyfin

(ein “normaler” Login dieses jellyfin Users wird mittels --disabled-password ausgeschlossen. Somit muss man kein Passwort vergeben und sich auch keine Gedanken um einen weiteren Angriffsvektor über einen Benutzeraccount machen. Der pseudo-Login wird über su ausgeführt.)

Damit auch der jellyfin User später die vom Container ausgegebenen Logs lesen kann, wird er zur systemc-journal Gruppe hinzugefügt:

usermod -aG systemd-journal jellyfin

Im Homeverzeichnis des neuen Benutzers (/opt/jellyfin) wird ein neues Bash-Script “init-jellyfin.sh” angelegt. Dieses dient nur dem ersten Start (und weiteren Starts nach einer Konfigurationsänderung). Nach dem ersten Start wird das Script aber idR. nicht mehr benutzt, sondern auf den systemd-basierten Autostart des Podman-Containers zurückgegriffen. Inhalt der Scriptdatei:

podman run \
	--name jellyfin \
	--detach \
	--label "io.containers.autoupdate=registry" \
	--volume jellyfin-cache:/cache:Z \
	--volume jellyfin-config:/config:Z \
	--mount type=bind,source=/mnt/raid2tb/media,destination=/media,ro=true \
	--net=host \
	docker.io/linuxserver/jellyfin:latest

(Pfad /mnt/raid2tb/media ggf. anpassen und zu eigenem Medienbibliothek-Pfad ändern!)

Das fehlen von --publish Argumenten für das Durchreichen von Containerports und die Angabe von --net=host sind hier entscheidend für die DLNA-Funktion. Damit der integrierte DLNA-Server im lokalen Netzwerk funktionieren kann, muss sich der Container im Host-Netzwerk befinden und darf nicht an ein separates Netzwerk angebunden sein.

Die Datei wird nun ausführbar gemacht und gestartet:

chmod u+x create-jellyfin.sh
./create-jellyfin.sh

Im Hintergrund werden die Containerimages heruntergeladen und die darin befindlichen Applikationn gestartet. Das kann auf einem SBC wie dem Rock64 einige Momente dauern. Wird die Kommandozeile ohne Fehlermeldung wieder freigegeben, scheint alles funktioniert zu haben. Im Webbrowser sollte dann unter http://<server.ip>:8096 das Webinterface zu sehen sein.

systemd Service für Autostart erstellen

Mit den aktuellen Einstellungen wird der Container nach einem Server-Neustart nicht mehr automatisch gestartet und müsste händisch mittels

podman start jellyfin 

neu gestartert werden. Das können wir mit einem systemd User-Service ändern.

Systemd erlaubt es, benutzerspezifische Services anzulegen und zu starten. Während der Jellyfin-Container im Hintergrund noch läuft, können wir über ein spezielles Podman-Kommando automatisch einen passenden systemd Service generieren lassen. Das geht so:

mkdir -p ~/.config/systemd/user/
podman generate systemd --new --name jellyfin > ~/.config/systemd/user/jellyfin.service 

Im nächsten Schritt würde man den neuen Service normalerweise mittels systemctl --user enable jellyfin aktivieren. Da wir zu Beginn einen jellyfin Account ohne Passwort angelegt und uns mittels su als dieser neue Benutzer eingeloggt haben, muss zunächst noch eine Korrektur vorgenommen werden.

Würde man an dieser Stelle das genannte systemd Kommando ausführen, erschiene die Fehlermeldung

Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined

Das kommt daher, dass wir uns mit dem jellyfin User nicht regulär via SSH oder ein TTY eingeloggt haben. Deshalb wurde der systemd user Service für den Benutzer nicht aktiviert und er verfügt über keine gültige Konfiguration des DBus-Zugriffs. Mit den folgenden Zeilen und Kommandos kann das Problem beseitigt werden:

In ~/.profile am Ende hinzufügen:

# This is for enabling "systemctl --user" commands without valid login session
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"

Nun zum root User wechseln (z.B. durch exit) - dann als root User ausführen:

loginctl enable-linger $(id -u jellyfin)
systemctl start user@$(id -u jellyfin).service

Zurück zum jellyfin User (su - jellyfin) - und der Service wird aktiviert:

systemctl --user daemon-reload
systemctl --user enable --now jellyfin.service

Das sollte nun ohne Fehlermeldungen funktionieren. Solltet ihr eine zsh Shell verwenden, muss noch mittels source ~/.profile die .profile Datei gesourced werden. Alternativ lassen sich die oben genannten export Zeilen selbstverständlich auch in die ~/.zshrc Datei unten einbetten.

WebUI: Medienbibliotheken einrichten

Alles weitere wird im Webfrontend von Jellyfin eingerichtet. Das Initialsetup wird euch zu euren Wunschbibliotheken befragen. Hier gilt es zu beachten, dass der Medienpfad als Pfad innerhalb des Containers angegeben wird - und das ist idR. immer /media. Egal, wie der Pfad zu euren Medien außerhalb des Containers lauten mag. Die beiden Pfade wurden im init-jellyfin.sh Script aufeinander gemapped.

Extra: Webinterface mit Nginx Proxy

Wer seine Jellyfish-Instanz auf Port 80 und / oder über eine sichere HTTPS Verbindung bereitstellen will, kann sich von der Beispielkonfiguration unter https://jellyfin.org/docs/general/networking/nginx.html inspirieren lassen.

Die Einrichtung wird vor allem dann empfohlen, wenn ihr außerhalb des heimischen Netzwerks mit Jellyfin kommunizieren wollt. So umgeht ihr auch elegant Portsperren für nicht-HTTP Ports in Hotel WLANs und sorgt dafür, dass niemand euren Traffic belauscht.

Ein Wort zur Performance

Wunder darf man von einem kleinen Single Board Computer wie dem Rock64 nicht erwarten. Sobald der Medienscan abgeschlossen ist, reagiert die Weboberfläche zwar nicht raasend schnell, aber angemessen zügig. Was die Live-Transcodierung von Videos angeht, wird man aber enttäuscht: Wie sich herausstellt, bringt Jellyfin zwar alles mit, was es zum Hardware-Kodierung braucht, soch das Rock64 Board ist dafür nicht geschaffen und kann verschiedene Mediencodecs bestenfalls in Hardware decodieren - nicht aber codieren.

Wenn höher aufgelöste Filme gestreamt werden sollen und das Zielgerät das Format nicht direkt unterstützt, stößt man schnell an Grenzen. Der Filme-Fan greift also lieber zu einem x86-basierten Computer mit einem halbwegs starken Prozessor, der die fraglichen Codecs wie H264 und H265 sowie VP9 in Hardware codieren kann.

Da ich vorhabe, fast ausschließlich Musik zu streamen, fällt dieser Nachteil für mich nicht besonders ins Gewicht.