medialab Server in einem Container installieren

medialab Server wird als Container-Image für linux/amd64 und linux/arm64 veröffentlicht:

ghcr.io/cristiancasapu/medialab-server

Das Image wird aus denselben signierten Debian-Paketen gebaut wie das apt-Repository (medialab-server, medialab-server-web, jellyfin-ffmpeg8); seine Build-Dateien sind öffentlich auf github.com/CristianCasapu/medialab-server-docker (GPL-2.0).

Tags:

Tag Bedeutung
12.2-ml.10 eine Version; empfohlen, wenn du selbst bestimmen willst, wann du aktualisierst
12.2 die neueste Version auf Basis von Jellyfin 12.2
latest die neueste Version
12.2-ml10-deb12 die exakte apt-Version im Image

Was du brauchst

  • Docker (oder Podman, Unraid, Portainer, Kubernetes) auf einem 64-Bit-x86- oder ARM-Rechner.
  • Ein Ordner für die Einstellungen (/config), einer für den Cache (/cache) und dein Medienordner (/media).
  • Die numerische UID und GID, die Eigentümer der Einstellungen sein soll: id -u und id -g dieses Kontos (PUID, PGID).

docker run

docker run -d --name medialab-server --restart unless-stopped \
  -p 8096:8096 -p 7359:7359/udp \
  -v /srv/medialab/config:/config \
  -v /srv/medialab/cache:/cache \
  -v /srv/media:/media:ro \
  -e PUID=1000 -e PGID=1000 -e TZ=Europe/Bucharest \
  ghcr.io/cristiancasapu/medialab-server:latest

Öffne http://<server>:8096. Auf einem neuen Server fragt der Einrichtungsassistent nach dem ersten Administrator, der Sprache und deinen Bibliotheken (füge sie aus /media hinzu).

Um den Assistenten zu überspringen, lass den Container den Administrator anlegen. Schreib das Passwort in eine Datei, nicht in die Befehlszeile:

printf '%s' 'a-long-password' > /srv/medialab/admin_password
docker run -d ... \
  -v /srv/medialab/admin_password:/run/secrets/admin_password:ro \
  -e MLW_ADMIN_USER=admin \
  -e MLW_ADMIN_PASSWORD_FILE=/run/secrets/admin_password \
  ghcr.io/cristiancasapu/medialab-server:latest

Das funktioniert nur auf einem neuen Server; auf einem bereits eingerichteten Server werden die Variablen ignoriert. Das Passwort erscheint nie in den Container-Logs.

Docker Compose

compose.yaml:

services:
  medialab-server:
    image: ghcr.io/cristiancasapu/medialab-server:12.2-ml.10
    container_name: medialab-server
    restart: unless-stopped
    ports:
      - "8096:8096"
      - "7359:7359/udp"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /srv/media:/media:ro
    environment:
      PUID: 1000
      PGID: 1000
      TZ: Europe/Bucharest
      MLW_SERVER_NAME: Home media
      MLW_ADMIN_USER: admin
      MLW_ADMIN_PASSWORD_FILE: /run/secrets/admin_password
    secrets:
      - admin_password

secrets:
  admin_password:
    file: ./secrets/admin_password
mkdir -p secrets && printf '%s' 'a-long-password' > secrets/admin_password
docker compose up -d
docker compose logs -f

Ein vollständiges Beispiel mit einer .env-Datei liegt im Repository (examples/compose.yaml, examples/.env.example).

Unraid

  1. Wähle im Docker-Tab Add Container und setze Template auf die URL https://raw.githubusercontent.com/CristianCasapu/medialab-server-docker/main/examples/unraid/medialab-server.xml (oder kopiere die Datei nach /boot/config/plugins/dockerMan/templates-user/).
  2. Prüfe die Pfade: /config → /mnt/user/appdata/medialab-server, /media → deine Medienfreigabe.
  3. PUID 99 und PGID 100 sind die üblichen Unraid-Werte (nobody / users).
  4. Intel- oder AMD-GPU: Füge --device=/dev/dri zu Extra Parameters hinzu und setze Hardware acceleration auf qsv oder vaapi. NVIDIA: Installiere das Nvidia-Driver-Plugin, füge --runtime=nvidia hinzu und setze es auf nvenc.
  5. Auf Apply, dann die WebUI öffnen.

Portainer

  1. Stacks → Add stack → Web editor und füge examples/portainer/stack.yml aus dem Repository ein.
  2. Lade unter Environment variables examples/portainer/stack.env (oder füge die Variablen von Hand hinzu) und passe die Pfade, PUID, PGID und TZ an.
  3. Deploy the stack.

Portainer-Stacks können auf einem eigenständigen Host keine Docker-Secrets nutzen; das Beispiel übergibt MLW_ADMIN_PASSWORD daher als Stack-Variable. Sie wird nur einmal verwendet; du kannst sie nach dem ersten Start löschen.

Kubernetes

examples/kubernetes/medialab-server.yaml enthält eine ConfigMap, zwei PersistentVolumeClaims, ein Deployment (ein Replikat, Strategie Recreate, Health-Probes auf /health), einen Service und einen Ingress:

kubectl create namespace medialab
kubectl -n medialab create secret generic medialab-secrets \
  --from-literal=admin-password='a-long-password' --from-literal=license-key='' --from-literal=repo-key=''
kubectl -n medialab apply -f medialab-server.yaml

Passe zuerst die Speichergrößen, das Medien-Volume (ein NFS-Beispiel ist enthalten) und den Hostnamen an. Betreibe nur ein Replikat: Der Server legt seine Datenbank in /config ab.

Hardware-Beschleunigung

Intel (QSV / VAAPI) und AMD (VAAPI)

docker run ... --device /dev/dri:/dev/dri -e MLW_HW_ACCEL=qsv ...     # Intel
docker run ... --device /dev/dri:/dev/dri -e MLW_HW_ACCEL=vaapi ...   # AMD or older Intel

Der Container fügt den Server-Benutzer selbst den Gruppen hinzu, denen /dev/dri/* gehört; du musst also nicht die Gruppennummern von render und video nachschlagen. Die Treiber kommen mit jellyfin-ffmpeg8; das amd64-Image enthält außerdem Intels OpenCL-Runtime aus Debian 12 für HDR-Tone-Mapping (sehr neue Intel-GPUs brauchen eventuell eine neuere Runtime, als Debian 12 sie enthält).

NVIDIA (NVENC / NVDEC)

Installiere den NVIDIA-Treiber und das NVIDIA Container Toolkit auf dem Host, dann:

docker run ... --gpus all -e MLW_HW_ACCEL=nvenc ...

Füge bei Compose examples/compose.nvidia.yaml hinzu; fordere in Kubernetes nvidia.com/gpu: 1 an.

Prüfe nach dem ersten Start Dashboard → Playback → Transcoding und spiele ein Video ab, das transkodiert werden muss; das ffmpeg-Log in /config/log zeigt, ob die GPU genutzt wurde.

Plugins, Einstellungen und Lizenzen von außen

Alles Folgende ist optional; die vollständige Liste der Variablen steht in docs/ENVIRONMENT.md im Repository.

Server-Einstellungen. Zum Beispiel:

-e MLW_PUBLISHED_URL=https://media.example.org \
-e MLW_KNOWN_PROXIES=172.16.0.0/12 \
-e MLW_UI_CULTURE=en-US -e MLW_METADATA_COUNTRY=US \
-e MLW_REMOTE_ACCESS=true

Sie werden vor dem Start des Servers in system.xml, network.xml und encoding.xml geschrieben. Mit der Voreinstellung MLW_CONFIG_MODE=first-run wird ein Wert auf einem neuen Server geschrieben und danach nur noch, wenn du die Variable änderst; was du später im Dashboard änderst, bleibt also erhalten. MLW_CONFIG_MODE=enforce schreibt sie bei jedem Start. Jede andere Eigenschaft lässt sich mit MLW_SYSTEM__<Property>, MLW_NETWORK__<Property> und MLW_ENCODING__<Property> setzen.

Plugin-Katalog. Der öffentliche medialab.works-Katalog und Jellyfins offizielles Repository sind standardmäßig hinzugefügt (MLW_PLUGIN_REPOS=medialab,jellyfin). Wenn dein Konto einen Repository-Schlüssel hat, füge ihn mit MLW_REPO_KEY (oder MLW_REPO_KEY_FILE) hinzu; er wird in den Logs maskiert.

Plugins. Liste sie in MLW_PLUGINS auf; sie werden installiert, bevor der Server startet:

-e MLW_PLUGINS="jf-rosub,jf-registration@1.0.2"

Ein Plugin wird über seinen Slug (jf-rosub), seinen Namen oder seine GUID gefunden; @version stellt eine Version fest. Das Archiv wird anhand der Prüfsumme im Katalog überprüft, und die medialab-Signatur (.dll.sig) bleibt erhalten, sodass das Dashboard das Plugin als verifiziert anzeigt. Bereits installierte Plugins werden nicht erneut heruntergeladen; MLW_PLUGINS_UPDATE=true installiert beim Start neuere Versionen.

Plugin-Einstellungen. Hänge entweder die Konfigurationsdatei des Plugins ein (/config/plugins/configurations/<Assembly>.xml, z. B. Rosub.xml) oder setze einzelne Werte:

-e MLW_PLUGIN_ROSUB__ArchiveCacheDays=14

Lizenzschlüssel. MLW_LICENSE_KEY, MLW_LICENSE_KEY_<SLUG> (z. B. MLW_LICENSE_KEY_JF_ROSUB), ihre _FILE-Formen und Docker-Secrets namens mlw_license_key oder mlw_license_key_<slug> werden unverändert an den Server-Prozess übergeben. Plugins lesen sie daraus in den Plugin-Versionen, die das unterstützen; bei einer älteren Plugin-Version gibst du den Schlüssel auf der Plugin-Seite im Dashboard ein (er wird in /config gespeichert).

Jede Variable akzeptiert auch <NAME>_FILE=/path/to/file, für Docker- und Kubernetes-Secrets.

Updates

Lade das neue Image herunter und erstelle den Container neu; die Einstellungen in /config bleiben erhalten:

docker pull ghcr.io/cristiancasapu/medialab-server:12.2-ml.10
docker stop medialab-server && docker rm medialab-server
docker run -d ... ghcr.io/cristiancasapu/medialab-server:12.2-ml.10     # dieselben Optionen wie zuvor

Mit Compose: Ändere den Tag in compose.yaml (oder behalte latest bei), dann docker compose pull && docker compose up -d. Auf Unraid: Check for Updates im Docker-Tab. In Portainer: Recreate mit Re-pull image.

Der In-App-Updater des Debian-Pakets ist nicht Teil des Images. Self-Update by MediaLab zeigt die verfügbaren Versionen in einem Container an, installiert aber keine Pakete; aktualisiere, indem du den neuen Image-Tag (12.2-ml.N) lädst und den Container neu erstellst.

Backup

/config enthält alles Wichtige (Einstellungen, Benutzer, Datenbank, Plugins, Metadaten). Stoppe den Container und sichere den Ordner:

docker stop medialab-server
tar -C /srv/medialab -czf medialab-config-$(date +%F).tar.gz config
docker start medialab-server

/cache kann neu aufgebaut werden und braucht kein Backup. Zum Wiederherstellen lege config wieder an seinen Platz und starte denselben Image-Tag oder einen neueren.

Fehlerbehebung

  • Sieh nach, was der Container gemacht hat: docker logs medialab-server. Zeilen, die mit [medialab] beginnen, stammen vom Startskript des Containers (geschriebene Einstellungen, installierte Plugins, angelegter Administrator).
  • Der Container stoppt sofort mit [medialab] ERROR: Eine Variable hat einen ungültigen Wert; die Meldung nennt sie.
  • „Permission denied“: PUID/PGID passen nicht zum Eigentümer der Ordner. Der Container korrigiert /config und /cache selbst, niemals /media. Wurde er mit --user gestartet, ändert er nichts: Die Ordner müssen bereits diesem Benutzer gehören.
  • Eine Einstellung aus der Umgebung wird nicht übernommen: Im Modus first-run wird ein Wert auf einem neuen Server geschrieben oder wenn er sich ändert. Nutze MLW_CONFIG_MODE=enforce, um ihn zu erzwingen.
  • Ein Plugin wird nicht installiert: Das Log sagt warum (Katalog nicht erreichbar, Name nicht gefunden, Prüfsumme stimmt nicht, keine Version für diesen Server). Der Server startet trotzdem, außer bei MLW_PLUGINS_STRICT=true.
  • Der Container wird als unhealthy angezeigt: Der Health-Check ruft /health auf Port 8096 (oder dem Port und der Basis-URL in network.xml) auf. Der erste Start kann ein bis zwei Minuten dauern.
  • Hinter einem Reverse Proxy bekommen Clients die falsche Adresse: Setze MLW_KNOWN_PROXIES auf die Adresse des Proxys und MLW_PUBLISHED_URL auf die öffentliche URL.