Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Docker 18.10.2026 · 12 min Lesezeit

LibreTV mit Docker auf Ubuntu: Installation hinter Reverse Proxy

LibreTV 2.19.5 per Docker Compose hinter Caddy oder Nginx betreiben: Passwort, PROXY_SECRET, Secure-Cookie über HTTPS, eigene Live-Quellen und die Rechtslage.

WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Titelbild: LibreTV mit Docker hinter einem Reverse Proxy

LibreTV mit Docker ist schnell installiert: ein Container, ein Passwort, Port 8080. Damit der Login sicher funktioniert und das Passwort nicht im Klartext durchs Netz geht, gehört ein Reverse Proxy mit HTTPS davor. Diese Anleitung zeigt die Einrichtung von LibreTV 2.19.5 auf einem Ubuntu-Server mit Docker Compose und Caddy, erklärt das Zusammenspiel von Secure-Cookie, X-Forwarded-Proto und COOKIE_SECURE und ordnet die rechtliche Seite ein.

Voraussetzungen

  • Server mit Ubuntu 24.04 LTS (oder Debian 12), x86_64 oder ARM64. Das Image wird laut README für linux/amd64 und linux/arm64 gebaut; der Docker-Teil dieser Anleitung ist nicht an eine Distribution gebunden.
  • Ein kleiner Server genügt: Im Test belegte LibreTV nach dem Start rund 48 MiB RAM, Caddy rund 68 MiB. Live-Streams über den LibreTV-Proxy (Schritt 7) kosten zusätzlich Bandbreite.
  • Docker Engine mit Docker Compose Plugin. Auf Ubuntu installieren Sie beides nach der offiziellen Docker-Anleitung für Ubuntu aus dem apt-Repository von Docker; Pakete wie docker.io oder docker-compose-v2 entfernen Sie laut Doku vorher. Siehe auch die Docker- und Compose-Grundlage.
  • Eine (Sub-)Domain, deren DNS-Eintrag auf den Server zeigt, und die Ports 80 und 443 frei. Wichtig bei ufw: Laut Docker-Doku umgehen veröffentlichte Container-Ports die ufw-Regeln; LibreTV lauscht hier deshalb nur auf 127.0.0.1. Für ein reines Heimnetz genügt ein interner Name mit internem Zertifikat.
  • Eigene oder lizenzierte Quellen, siehe den rechtlichen Hinweis weiter unten.

Eckdaten im Überblick

PunktWert
Projekt, LizenzLibreSpark/LibreTV, AGPL-3.0, Next.js 15 (getestet 2.19.5 mit Next.js 15.5.26, Node.js 22.23.3)
Imageghcr.io/librespark/libretv, Spiegel docker.io/bestzwei/libretv; Tags latest, 2.19, 2.19.5 (am 18.10.2026 identischer Digest, für 2.19.5 auch auf Docker Hub)
Port im Container8080 (HTTP)
Volumeskeine; Verlauf und Einstellungen liegen im Browser
Pflicht-VariablePASSWORD
EmpfohlenPROXY_SECRET (Signaturschlüssel der Sitzungen)
OptionalCOOKIE_SECURE, LIVE_ALLOW_PRIVATE, REQUEST_TIMEOUT, DEFAULT_LIVE_SOURCES
Benutzer im Containernextjs (UID 100), kein root

Rechtlicher Hinweis: welche Quellen erlaubt sind

LibreTV speichert selbst keine Videos. Laut README ist es ein Aggregator für sogenannte Sammelseiten (Apple-CMS-Schnittstellen) und für M3U-Listen, und für die Rechtmäßigkeit der Inhalte sind die jeweiligen Quellen verantwortlich. Fremde Quellen nennt diese Anleitung bewusst nicht.

Für Deutschland und die EU ist die Lage klar: Der Europäische Gerichtshof hat im Urteil C-527/15 „Filmspeler“ vom 26. April 2017 entschieden, dass vorübergehende Vervielfältigungen durch Streaming von der Website eines Dritten, auf der ein Werk ohne Erlaubnis des Rechteinhabers angeboten wird, auf einem multimedialen Medienabspieler nicht unter die Ausnahme des Art. 5 Abs. 1 und 5 der Richtlinie 2001/29/EG fallen. Die Privatkopie nach § 53 Abs. 1 UrhG ist ausgeschlossen, wenn eine „offensichtlich rechtswidrig hergestellte oder öffentlich zugänglich gemachte Vorlage“ verwendet wird. § 106 UrhG stellt das Vervielfältigen, Verbreiten oder öffentliche Wiedergeben ohne Einwilligung des Berechtigten unter Strafe.

Sinnvoll und zulässig ist LibreTV deshalb nur mit Quellen, für die Rechte bestehen: eigene Aufnahmen und Medien, die Sie selbst als HLS bereitstellen, frei lizenzierte Streams oder ein IPTV-Angebot, das Sie vertraglich nutzen dürfen. Für eine reine Mediathek eigener Filme ist ein Medienserver wie Jellyfin mit Docker oft die bessere Wahl. Der Funktionstest in dieser Anleitung lief ausschließlich mit einem selbst erzeugten Testbild-Stream.

Schritt 1: Projektordner und Geheimnisse anlegen

Legen Sie einen Ordner an und erzeugen Sie Passwort und Signaturschlüssel. Ohne PROXY_SECRET leitet LibreTV den Schlüssel laut Quellcode aus dem Passwort ab. Ein eigener Wert trennt beides.

sudo mkdir -p /opt/libretv && sudo chown "$USER": /opt/libretv && cd /opt/libretv
cat > .env <<EOF
PASSWORD=$(openssl rand -base64 18)
PROXY_SECRET=$(openssl rand -hex 32)
EOF
chmod 600 .env
grep PASSWORD .env

Das Passwort ist das einzige Zugangsmerkmal der Instanz; es gibt keine Benutzerkonten. Alle, die das Passwort kennen, sehen dieselbe Oberfläche, die Quellen speichert aber jeder Browser für sich.

Verifizieren: cat .env zeigt zwei Zeilen mit zufälligen Werten, ls -l .env zeigt die Rechte -rw-------.

Schritt 2: LibreTV mit Docker Compose und Caddy

LibreTV lauscht nur auf 127.0.0.1, von außen ist allein Caddy erreichbar. Das ist mehr als Kosmetik: Die Login-Sperre nach zehn Fehlversuchen richtet sich nach der Client-IP aus X-Forwarded-For. Im Test ließ sich die Sperre direkt auf Port 8080 mit einem gefälschten Header umgehen (wieder 401 statt 429), über Caddy dagegen nicht.

services:
  libretv:
    image: ghcr.io/librespark/libretv:${LIBRETV_TAG:-2.19.5}
    container_name: libretv
    restart: unless-stopped
    environment:
      PASSWORD: ${PASSWORD:?PASSWORD in .env setzen}
      PROXY_SECRET: ${PROXY_SECRET:?PROXY_SECRET in .env setzen}
    ports:
      - "127.0.0.1:8080:8080"

  caddy:
    image: caddy:2.11
    container_name: libretv-caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - libretv

volumes:
  caddy_data:
  caddy_config:

Die Schreibweise ${PASSWORD:?…} sorgt dafür, dass Compose ohne Passwort gar nicht erst startet. Im Test lautete die Meldung bei fehlender .env: required variable PASSWORD is missing a value: PASSWORD in .env setzen. Der Tag ist fest auf 2.19.5 gesetzt; mit latest würde jeder Pull auf die jeweils neueste Version wechseln.

Verifizieren: docker compose config gibt die zusammengesetzte Konfiguration ohne Fehlermeldung aus; fehlt die .env, bricht der Befehl mit der oben genannten Meldung ab.

Schritt 3: Caddyfile schreiben

Caddy holt für eine öffentlich erreichbare Domain selbst ein Zertifikat und leitet HTTP auf HTTPS um. Ersetzen Sie tv.example.de durch Ihren Namen:

tv.example.de {
	reverse_proxy libretv:8080
}

Für einen Server nur im Heimnetz ergänzen Sie in dem Block die Zeile tls internal; Caddy stellt dann ein Zertifikat seiner eigenen Zertifizierungsstelle aus, dem Sie auf den Clients einmal vertrauen müssen. Diese Variante lief im Test. Caddy setzt laut Doku X-Forwarded-Proto und X-Forwarded-For selbst und ignoriert solche Header vom Client. Mehr zu Zertifikaten und mehreren Domains steht in der Anleitung Caddy als Reverse Proxy mit automatischem HTTPS.

Verifizieren: docker run --rm -v "$PWD/Caddyfile:/etc/caddy/Caddyfile:ro" caddy:2.11 caddy validate --config /etc/caddy/Caddyfile endet mit Valid configuration.

Schritt 4: Stack starten und Version prüfen

docker compose pull
docker compose up -d
docker compose ps
docker compose logs libretv

Das Log ist kurz. Im Test stand dort:

   ▲ Next.js 15.5.26
   - Local:        http://localhost:8080
   - Network:      http://0.0.0.0:8080

 ✓ Starting...
 ✓ Ready in 116ms

Die laufende Version liefert der Endpunkt /api/status, auch ohne Login:

curl -s http://127.0.0.1:8080/api/status
{"passwordRequired":true,"verified":false,"version":"2.19.5","defaultSources":[],"defaultLiveSources":[],"defaultSubscriptions":[],"defaultRecommendSource":null,"defaultImageMode":null}

Verifizieren: docker compose ps zeigt beide Container als Up, version ist 2.19.5, curl -I http://tv.example.de antwortet mit 308 Permanent Redirect auf HTTPS.

Öffnen Sie https://tv.example.de. Die Oberfläche ist chinesisch; vor der Startseite erscheint der Dialog „访问验证“ (Zugangsprüfung) mit einem Passwortfeld und der Schaltfläche „进入“ (Eintreten).

LibreTV-Startseite mit Passwortdialog vor dem Login
Vor dem Login blendet LibreTV den Passwortdialog über die Startseite.

Nach erfolgreichem Login setzt LibreTV das Cookie ltv_session für 90 Tage. Ob es das Secure-Flag trägt, entscheidet Version 2.19.5 so: COOKIE_SECURE=true oder false erzwingt den Wert, sonst zählt der Header X-Forwarded-Proto. Der Warnhinweis im README beschreibt dagegen ein pauschales Secure-Cookie im Produktivbetrieb; der Test ergab folgendes Bild:

AufrufSet-CookieLogin hält?
Caddy, HTTPSSecure; HttpOnly; SameSite=laxja
Nginx mit X-Forwarded-Proto $schemeSecure; HttpOnly; SameSite=laxja
Nginx mit TLS, ohne X-Forwarded-ProtoHttpOnly; SameSite=lax (kein Secure)ja, Cookie aber ungeschützt
direkt http://…:8080, ohne VariableHttpOnly; SameSite=laxja, Passwort im Klartext
direkt http://…:8080 mit COOKIE_SECURE=trueSecure; HttpOnly; SameSite=laxnein

Der letzte Fall ist das bekannte Fehlerbild „Passwort richtig, aber Login klappt nicht“: Der Server antwortet mit 200 und {"success":true}, der Client verwirft das Secure-Cookie auf einer HTTP-Seite, /api/status meldet danach "verified":false. Mit curl lässt sich das nachstellen, wenn Sie einen echten Hostnamen statt localhost verwenden (Browser nehmen localhost von der Regel aus):

curl -s -i --resolve tv.example.test:8080:127.0.0.1 -c jar \
  -H 'Content-Type: application/json' -d '{"password":"IHR-PASSWORT"}' \
  http://tv.example.test:8080/api/auth | grep -i set-cookie
grep -c ltv_session jar      # 0 = Cookie verworfen

Hinter Caddy brauchen Sie COOKIE_SECURE nicht. Setzen Sie COOKIE_SECURE=true laut README dann, wenn ein vorgeschalteter Dienst X-Forwarded-Proto nicht weitergibt und Sie trotzdem ein Secure-Cookie wollen, aber nur, wenn die Seite ausschließlich per HTTPS erreichbar ist.

Verifizieren: Der Login über https:// liefert Set-Cookie: ltv_session=…; Secure; HttpOnly; SameSite=lax, danach zeigt https://tv.example.de/api/status "verified":true.

Schritt 6: Startseite und Einstellungen

Die Einstellungen öffnen Sie über das Zahnrad oben rechts (设置). Das Panel hat drei Reiter: 源管理 (Quellenverwaltung), 偏好设置 (Vorlieben) und 数据 (Daten). Nach der Installation ist die Liste der Videoquellen (点播源) leer.

Die Startseite zeigt ab Werk Ranglisten mit Filmtiteln und Postern aus öffentlichen Diensten. Wer das nicht möchte, schaltet unter 偏好设置, Bereich 首页与内容, den Schalter 首页推荐 (Empfehlungen auf der Startseite) aus. Der Filter für Erwachseneninhalte (成人内容过滤) ist ab Werk aktiv.

LibreTV-Einstellungen mit deaktivierten Startseiten-Empfehlungen
Unter 偏好设置 und 首页与内容 lassen sich die Startseiten-Empfehlungen abschalten.

Alle Einstellungen und Quellen speichert LibreTV im Browser (localStorage, Verlauf in IndexedDB), nicht auf dem Server. Ein anderer Browser startet daher leer. Zum Übertragen gibt es laut README unter 数据 einen Import und Export der Konfiguration; Administratoren können Live-Quellen laut README zentral über DEFAULT_LIVE_SOURCES vorgeben.

Verifizieren: Nach dem Abschalten zeigt die Startseite nur noch Logo und Suchfeld, ohne Poster.

Schritt 7: Eigenen Stream als Live-Quelle einbinden

Live-Quellen sind M3U-Listen. Für den Test erzeugte ein ffmpeg-Container einen 60 Sekunden langen HLS-Stream mit Testbild (testsrc2, 1280 x 720) und eine passende Liste, ausgeliefert von einem Nginx-Container namens hls im selben Compose-Netz:

#EXTM3U
#EXTINF:-1 tvg-id="test" group-title="Test",Eigener Teststream
http://hls/index.m3u8

Liegt die Quelle in einem privaten Netz (Docker-Netz, LAN, eigener IPTV-Server), blockt LibreTV sie zum Schutz vor SSRF. Der Test lieferte ohne weitere Einstellung 403 mit {"error":"目标地址解析到私有/保留网络"} („Ziel löst auf privates oder reserviertes Netz auf“). Ergänzen Sie für interne Quellen in der compose.yaml unter environment:

      LIVE_ALLOW_PRIVATE: "1"

Danach docker compose up -d. Die Freigabe gilt laut Kommentar in der docker-compose.yml des Projekts für Listenabruf, Erreichbarkeitstest und Streamproxy. Setzen Sie sie nur, wenn Sie interne Quellen wirklich brauchen, denn angemeldete Nutzer können LibreTV damit Adressen im internen Netz abrufen lassen.

In der Oberfläche fügen Sie die Liste unter 设置, 源管理, 直播源 (Live-Quellen) mit 添加直播源 hinzu: Adresse der M3U-Datei, optional ein Name. LibreTV lädt die Liste sofort und zeigt die Zahl der Kanäle.

LibreTV-Live-Quellen mit eigener M3U-Liste und einem erkannten Kanal
Die eigene M3U-Liste ist eingetragen, LibreTV erkennt einen Kanal.

Unter 直播 (Live) oben in der Navigation erscheint der Kanal; ein Klick startet die Wiedergabe. Erreicht der Browser die Quelle nicht direkt, leitet LibreTV den Stream laut README über /api/live/stream durch den Server. Im Test lief so der Stream, der nur im Docker-Netz erreichbar war.

LibreTV-Live-Seite spielt den eigenen Testbild-Stream ab
Wiedergabe des selbst erzeugten Testbild-Streams auf der Live-Seite.

Verifizieren: Die Wiedergabe startet nach einem Klick auf den Kanal; im Test meldete der Erreichbarkeitstest /api/live/probe "ok":true und "level":"segment", und das Video lief nach zehn Sekunden bei Sekunde 9,6 in 1280 x 720.

Schritt 8: Nginx statt Caddy

Betreiben Sie schon Nginx, binden Sie LibreTV dort ein. Entscheidend ist die Zeile mit X-Forwarded-Proto, sonst fehlt dem Cookie das Secure-Flag. proxy_buffering off verhindert, dass Nginx die Stream-Antworten erst zwischenspeichert.

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
}

Im Test lief Nginx als Container im selben Compose-Netz, dort mit proxy_pass http://libretv:8080;. Läuft Nginx direkt auf dem Host, gilt die Adresse 127.0.0.1:8080 wie oben. Den vollständigen Server-Block mit Zertifikat beschreibt die Anleitung Nginx als Reverse Proxy mit TLS. Für Traefik siehe Traefik als Docker-Reverse-Proxy.

Verifizieren: nginx -t meldet syntax is ok, der Login-Aufruf liefert Set-Cookie mit Secure.

Schritt 9: Sichern und aktualisieren

LibreTV selbst hat keine Daten auf dem Server. Sichern Sie .env, compose.yaml und Caddyfile; das Volume caddy_data enthält die Zertifikate und lässt sich notfalls neu ausstellen. Die Quellen der Nutzer stecken in deren Browsern, dafür ist laut README der Export unter 数据 gedacht.

Behalten Sie PROXY_SECRET bei: Im Test machte ein neuer Wert alle bestehenden Sitzungen ungültig ("verified":false), mit dem alten Wert waren sie wieder gültig. Das ist zugleich der Weg, alle Geräte abzumelden.

Für ein Update setzen Sie den neuen Tag und ziehen das Image. Verfügbare Tags zeigt die Paketseite auf GitHub. Im Test lief der Wechsel von 2.19.2 auf 2.19.5 ohne Zwischenschritte:

sed -i '/^LIBRETV_TAG=/d' .env
echo "LIBRETV_TAG=2.19.5" >> .env
docker compose pull libretv
docker compose up -d libretv
curl -s http://127.0.0.1:8080/api/status | grep -o '"version":"[^"]*"'

Verifizieren: /api/status meldet die neue Version, im Test zuerst 2.19.2, nach dem Wechsel 2.19.5. Bestehende Sitzungen blieben im Test nach dem Neuerstellen des Containers gültig, solange PROXY_SECRET gleich blieb.

Troubleshooting

SymptomUrsacheLösung
Passwort richtig, Dialog erscheint erneutSecure-Cookie auf einer HTTP-Seite, etwa COOKIE_SECURE=true ohne HTTPSNur über HTTPS aufrufen oder Variable entfernen
尝试次数过多,请 10 分钟后再试, HTTP 429Mehr als zehn Fehlversuche in zehn Minuten von einer IPZehn Minuten warten oder Container neu starten (Zähler liegt laut Quellcode im Speicher)
密码错误, HTTP 401Falsches PasswortWert in .env prüfen, nach Änderung docker compose up -d
目标地址解析到私有/保留网络, HTTP 403Live-Quelle im internen NetzLIVE_ALLOW_PRIVATE: "1" setzen
未登录, HTTP 401 bei API-AufrufenKeine gültige Sitzung, etwa nach Wechsel von PROXY_SECRETNeu anmelden
Compose startet nicht, required variable PASSWORD is missing.env fehlt oder liegt im falschen Ordnerdocker compose im Projektordner ausführen

Häufige Fragen

Die Software selbst ja, sie steht unter AGPL-3.0 und enthält keine Inhalte. Die rechtliche Frage stellt sich bei den Quellen: Streaming von Angeboten, die ohne Erlaubnis des Rechteinhabers online stehen, fällt nach dem EuGH-Urteil C-527/15 nicht unter die Ausnahme für vorübergehende Vervielfältigungen. Mit eigenen oder lizenzierten Streams bleiben Sie innerhalb Ihrer Rechte.

Warum klappt der Login über die Server-IP nicht?

Meist trägt das Cookie das Secure-Flag, während die Seite per HTTP läuft; der Browser verwirft es. In 2.19.5 passiert das ohne Proxy nur mit COOKIE_SECURE=true oder einem falsch gesetzten X-Forwarded-Proto. Die richtige Lösung ist ohnehin HTTPS über einen Reverse Proxy.

Nein. Caddy meldet X-Forwarded-Proto: https, LibreTV setzt dann das Secure-Flag selbst. Die Variable ist für Proxys gedacht, die den Header nicht weitergeben.

Kann LibreTV mehrere Benutzer verwalten?

Nein. Es gibt ein gemeinsames Passwort und keine Konten. Verlauf, Favoriten und Quellen hängen am jeweiligen Browser.

Welche Version läuft gerade?

curl -s https://tv.example.de/api/status zeigt das Feld version, im Fußbereich der Oberfläche steht sie ebenfalls.

Gibt es die Oberfläche auf Deutsch?

In Version 2.19.5 ist die Oberfläche chinesisch. Die wichtigsten Begriffe: 设置 Einstellungen, 源管理 Quellenverwaltung, 直播源 Live-Quellen, 直播 Live, 偏好设置 Vorlieben, 数据 Daten.

Fazit

Technisch ist LibreTV schlank: ein zustandsloser Container ohne Volumes, wenige Variablen, Update per Tag-Wechsel. Wichtig sind drei Punkte: Port 8080 nur lokal binden, HTTPS über einen Reverse Proxy mit korrektem X-Forwarded-Proto und ein fester PROXY_SECRET. Inhaltlich steht und fällt der Nutzen mit den Quellen. Mit eigenen HLS-Streams oder einem lizenzierten IPTV-Zugang funktioniert LibreTV als schlanke Weboberfläche; für unlizenzierte Sammelseiten hilft auch die beste Technik rechtlich nicht weiter.

Weiterführende Anleitungen und Quellen