192.168.178.20:3001. 192.168.178.20:8096. 192.168.178.20:8000. 192.168.178.53. Vier Adressen, und das ist noch ein kleines Homelab: Uptime Kuma, Jellyfin, Paperless, Pi-hole. Irgendwann kennt man die Portnummern auswendig wie früher die Telefonnummern der Freunde, und irgendwann fragt jemand aus der Familie, wie man nochmal an die Filme kommt, und man sagt „acht null neun sechs“.
Die Lösung dafür heißt Reverse Proxy: ein einziger Dienst, der auf Port 80 und 443 wartet, sich den Namen ansieht, den der Browser aufgerufen hat, und die Anfrage an den richtigen Container weiterreicht. Was ein Reverse Proxy ist, steht hier schon ausführlich. Heute bauen wir einen. Nginx Proxy Manager ist dafür die Variante mit Weboberfläche: Der Reverse Proxy darunter ist Nginx, aber statt Konfigurationsdateien füllst du ein Formular mit vier Feldern aus.
kuma.home.arpa. Jeder weitere Dienst ist danach ein Formular und ein DNS-Eintrag. Dauer: rund 30 Minuten. Stufe: Standard, du kennst Docker und die Kommandozeile im Groben. Stand: Nginx Proxy Manager 2.15.1, geprüft am 15-09-2026.
Was Nginx Proxy Manager ist
Nginx Proxy Manager, in Foren meist nur NPM, ist ein Docker-Image, in dem Nginx als Reverse Proxy läuft und eine Weboberfläche davor sitzt. Du trägst ein: Dieser Name geht an diese IP und diesen Port. NPM schreibt daraus die Nginx-Konfiguration und lädt sie neu. Dazu kann es Zertifikate von Let's Encrypt holen und erneuern, Zugriffslisten mit Benutzername und Passwort vor einen Dienst legen, und für Dienste, die keinen Web-Verkehr sprechen, rohe TCP- und UDP-Ports weiterreichen. Das Projekt von jc21 steht unter MIT-Lizenz, die aktuelle Version 2.15.1 ist vom 03-06-2026, und Docker ist laut FAQ die einzige unterstützte Art, es zu betreiben.
Eine Sache hat sich im letzten Jahr geändert, und sie ist der Grund, warum viele ältere Anleitungen im Netz nicht mehr stimmen: Mit Version 2.13.0 vom 04-11-2025 bekam NPM eine neue Oberfläche, und der Standard-Benutzer admin@example.com mit dem Passwort changeme ist weg. Stattdessen legst du beim ersten Aufruf dein eigenes Konto an. Ich finde das richtig. Ein Passwort, das in jeder Anleitung der Welt steht, war nie eines, und die Release Notes sagen ausdrücklich, dass es für Automatisierer weiterhin Umgebungsvariablen dafür gibt.
Das Schaubild zeigt schon, dass zwei Dinge zusammenspielen müssen: Erst muss der Name zu deinem Server führen, das ist die Aufgabe von DNS. Dann muss NPM wissen, welcher Dienst hinter dem Namen steckt. Der erste Teil passiert in Pi-hole oder einem anderen Namensdienst, der zweite in NPM. Beide bauen wir.
Was du brauchst
- Einen Linux-Rechner mit Docker und Docker Compose v2. Das Image gibt es für amd64 und arm64; ein Raspberry Pi 4 oder 5 reicht. Ältere Pis mit 32-Bit-System (armv7) sind seit Version 2.14 raus, die Doku verweist sie auf 2.13.7. Wer Docker noch nicht hat: Bei Paperless-ngx steht der Weg.
- Die Ports 80, 443 und 81 frei auf diesem Rechner. 80 und 443 sind die Stelle, an der es am häufigsten hakt, weil dort oft schon etwas anderes lauscht.
- Einen Dienst, der schon läuft und ein Ziel abgibt. Ich nehme Uptime Kuma auf Port 3001; jeder andere Web-Dienst geht genauso.
- Einen Ort, an dem du Namen vergibst. Am saubersten ist Pi-hole, dort gilt der Name für alle Geräte im Haus. Ohne Pi-hole geht es mit der hosts-Datei auf einem einzelnen PC, dazu unten mehr.
Die prüfbaren Punkte klärst du mit drei Befehlen auf dem Server:
docker --version
docker compose version
sudo ss -tulpn | grep -E ':(80|81|443) 'Die ersten beiden Zeilen liefern je eine Versionsnummer. Die dritte bleibt leer; steht dort eine Zeile, lauscht auf einem der drei Ports schon ein Programm, und wer das ist, findest du hier heraus. Alle Befehle rechnen mit denselben Beispielwerten; wo sie unten auftauchen, setzt du deine ein:
- IP-Adresse des Docker-Rechners:
192.168.178.20 - IP-Adresse von Pi-hole:
192.168.178.53 - Der Dienst, der als Erstes einen Namen bekommt: Uptime Kuma auf
192.168.178.20:3001 - Der Name dafür:
kuma.home.arpa - Ordner für Nginx Proxy Manager:
~/nginx-proxy-manager - Zeitzone:
Europe/Berlin
Schritt 1: Ordner und Compose-Datei anlegen
NPM bekommt einen eigenen Ordner. Die zwei Unterordner darin legt Docker beim ersten Start selbst an.
mkdir -p ~/nginx-proxy-manager
cd ~/nginx-proxy-managerJetzt das Rezept. Es ist das Compose-Beispiel aus der Setup-Seite der NPM-Doku, mit einer einzigen Änderung: Die Zeitzone steht dort auf Brisbane, weil der Entwickler in Australien sitzt.
Datei anlegen:
nano docker-compose.ymlInhalt einfügen, mit Strg+O und Enter speichern, mit Strg+X schließen:
services:
app:
image: 'jc21/nginx-proxy-manager:2.15.1'
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
environment:
TZ: "Europe/Berlin"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencryptOb Compose die Datei versteht, zeigt eine Probe ohne Start:
docker compose config --quiet && echo OKKommt OK, stimmt die Einrückung. Kommt eine Fehlermeldung mit Zeilennummer, ist es fast immer ein Leerzeichen zu viel oder zu wenig.
Die drei Ports sind die Geschichte dieser Datei. Links steht die Nummer auf deinem Rechner, rechts die im Container. 80 und 443 sind die Ports, auf denen jeder Browser HTTP und HTTPS erwartet; deshalb tippst du später kuma.home.arpa ohne Doppelpunkt und Zahl, weil 80 der Port ist, den der Browser von selbst nimmt. Port 81 ist die Verwaltungsoberfläche von NPM, nur für dich. Die beiden Ordner darunter: In data liegt alles, was NPM weiß, die Datenbank mit deinen Hosts und Benutzern als database.sqlite und die daraus erzeugte Nginx-Konfiguration; in letsencrypt landen später die Zertifikate.
Die Versionsnummer im Image-Namen ist Absicht. Die Doku selbst setzt sie so, und beim Reverse Proxy würde ich es auch dann tun, wenn sie es nicht täte: An diesem Container hängen ab heute alle anderen. Ein Update, das beim nächsten docker compose pull unbemerkt eine neue Hauptversion zieht, legt im schlechtesten Fall alle Dienste auf einmal still. Mit der festen Nummer ist ein Update ein Handgriff, den du bewusst machst; wie, steht unten.
Schritt 2: Starten und das Admin-Konto anlegen
docker compose up -d
docker compose psIn der Spalte STATUS muss Up stehen. Beim allerersten Start erzeugt NPM laut Doku seine Schlüssel, legt die Datenbank an und braucht dafür je nach Rechner ein paar Minuten; in dieser Zeit lädt die Oberfläche noch nicht. Bleibt der Status bei Restarting, zeigt docker compose logs app den Grund, und der häufigste steht unten bei „Wo es klemmt“.
Im Browser http://192.168.178.20:81 öffnen. NPM begrüßt dich mit Welcome! und dem Satz Get started by creating your admin account. Drei Felder: Full Name, Email address, New Password; das Passwort braucht mindestens 8 Zeichen. Unten Save. NPM meldet dich mit diesem Konto sofort an, ohne zweite Anmeldeseite. Stand Nginx Proxy Manager 2.15.


Du landest auf dem Dashboard mit vier Kacheln: 0 Proxy Hosts, 0 Redirection Hosts, 0 Streams, 0 404 Hosts. Die erste Null verschwindet gleich. Die E-Mail-Adresse ist dein Anmeldename; NPM prüft nur, dass sie wie eine Adresse aussieht, eine echte muss es nicht sein.
Falls du stattdessen eine Anmeldeseite siehst und noch nie ein Konto angelegt hast: Dann liegt im Ordner data eine Datenbank aus einem früheren Versuch, und NPM hält die Einrichtung für erledigt. Wie du das auflöst, steht unten bei „Wo es klemmt“.
Schritt 3: Ein Name für den Dienst
NPM wartet jetzt auf Port 80, aber noch kennt kein Gerät im Haus den Namen kuma.home.arpa. Der Name muss zur IP des Docker-Rechners führen, und das ist die Aufgabe deines DNS-Servers, also von Pi-hole, wenn du eines betreibst.
home.arpa für Heimnetze und legt fest, dass Anfragen dazu nie ins Internet weitergereicht werden dürfen. Du kannst dir also jeden Namen davor ausdenken, ohne dass er irgendwo draußen mit einer echten Domain kollidiert. Die beliebte Endung .local ist dagegen durch RFC 6762 an ein anderes Verfahren vergeben, mDNS: Anfragen darauf schickt ein Gerät per Multicast ins eigene Netz statt an den DNS-Server, und Pi-hole kommt gar nicht erst zu Wort.Im Pi-hole-Dashboard http://192.168.178.53/admin links im Menü Settings, dann Local DNS Records. In der Box Local DNS records ins Feld Domain den Namen kuma.home.arpa eintragen, ins Feld IP die Adresse des Docker-Rechners 192.168.178.20, dann auf das + daneben. Der Eintrag erscheint in der Liste darunter. Stand Pi-hole 6.x.
Wichtig ist, was in das IP-Feld kommt: die Adresse des Rechners, auf dem NPM läuft, nicht die von Uptime Kuma und nicht irgendein Port. Der Name führt zu NPM; wohin es von dort weitergeht, entscheidet NPM im nächsten Schritt. Deshalb zeigen später alle deine Namen auf dieselbe IP, und das ist genau richtig so.
Ohne Pi-hole gibt es einen Notbehelf, der nur auf dem einen Rechner gilt, an dem du gerade sitzt: die hosts-Datei. Auf Linux und macOS hängst du im Terminal des PCs eine Zeile an:
echo "192.168.178.20 kuma.home.arpa" | sudo tee -a /etc/hostsUnter Windows: den Editor als Administrator starten (Startmenü, „Editor“ tippen, Rechtsklick, Als Administrator ausführen), darin die Datei C:\Windows\System32\drivers\etc\hosts öffnen, die Zeile 192.168.178.20 kuma.home.arpa ans Ende setzen, speichern.
Das Handy im WLAN kennt den Namen dann nicht; dafür brauchst du den Pi-hole-Weg.
Ob der Name ankommt, prüfst du von deinem PC aus:
ping -c 1 kuma.home.arpaPING kuma.home.arpa (192.168.178.20) 56(84) bytes of data.
64 bytes from 192.168.178.20: icmp_seq=1 ttl=64 time=0.41 msIn der Klammer muss die IP des Docker-Rechners stehen. Steht dort „Name or service not known“, hat dein PC den Eintrag nicht bekommen; meist fragt er dann gar nicht Pi-hole, sondern noch den Router, und das ist ein Fall für die FritzBox-Anleitung.
Schritt 4: Der erste Proxy Host
Jetzt die Regel selbst. Ein Proxy Host ist in NPM ein Eintrag der Form: Dieser Name wird an diese Adresse und diesen Port weitergereicht.
In NPM im Menü Hosts, dann Proxy Hosts, dann Add Proxy Host. Im Reiter Details: Bei Domain Names den Namen kuma.home.arpa eintragen. Scheme bleibt auf http. Bei Forward Hostname / IP die Adresse des Docker-Rechners 192.168.178.20, bei Forward Port die 3001. Die Schalter Block Common Exploits und Websockets Support einschalten, Cache Assets bleibt aus, Access List bleibt leer. Die Reiter Custom Locations, SSL und Settings lässt du heute in Ruhe; der letzte hat keine Beschriftung, nur ein Zahnrad-Symbol rechts neben SSL. Unten Save. In der Liste steht der Host jetzt mit dem Status Online. Stand Nginx Proxy Manager 2.15.



Zu den beiden Schaltern: Ein WebSocket ist eine Leitung, die nach dem ersten Aufruf offen bleibt, damit der Server von sich aus Neuigkeiten schicken kann, statt dass der Browser alle paar Sekunden nachfragt. Uptime Kuma arbeitet so, und sein Wiki sagt für NPM genau einen Satz: „Please enable WebSockets Support“. Was ohne den Schalter passiert, sagt es nicht; ich lasse ihn bei jedem Host an, denn einem Dienst, der keine WebSockets nutzt, tut er nichts. Block Common Exploits legt eine Liste aus dem NPM-Image vor den Dienst: Anfragen mit typischen SQL-Injection-Fetzen wie union select, mit fremden URLs oder Pfad-Tricks in den Parametern und von bekannten Angriffswerkzeugen weist Nginx ab, bevor sie den Dienst erreichen. Im Heimnetz ist das Nettigkeit, an einem Port, der später ins Internet zeigt, Pflicht.
Und jetzt der Moment, für den die Portnummern-Litanei am Anfang stand: Im Browser http://kuma.home.arpa eintippen, ohne Doppelpunkt, ohne Zahl. Uptime Kuma erscheint. Der Browser hat Pi-hole gefragt, Pi-hole hat auf den Docker-Rechner gezeigt, NPM hat auf Port 80 den Namen gelesen und die Anfrage an Port 3001 durchgereicht. Drei Beteiligte, und keiner davon musste neu gestartet werden.
Jeder weitere Dienst ist ab jetzt dasselbe Paar: ein Eintrag in Pi-hole, der auf 192.168.178.20 zeigt, und ein Proxy Host in NPM, der den Namen an den Port des Dienstes reicht. Jellyfin wird jellyfin.home.arpa auf 8096, Paperless paperless.home.arpa auf 8000. Was in das Feld Forward Hostname / IP gehört, ist die eine Stelle, an der man sich vertut, deshalb noch einmal ausdrücklich: die IP des Rechners, nicht 127.0.0.1 und nicht localhost. NPM läuft in einem Container, und in einem Container ist localhost der Container selbst. Dort läuft kein Uptime Kuma, und Nginx antwortet mit „502 Bad Gateway“.
HTTPS: was heute noch fehlt
Der Browser zeigt vor kuma.home.arpa weiter „Nicht sicher“, und das ist ehrlich: Der Verkehr läuft unverschlüsselt über HTTP, genau wie vorher über IP und Port. Neu ist nur der Name. Für ein Schloss im Browser braucht es ein Zertifikat, und Let's Encrypt stellt eines nur für Domains aus, die dir gehören und die im öffentlichen DNS stehen; für eine ausgedachte home.arpa-Adresse gibt es keins.
NPM hat dafür im Reiter SSL zwei Wege, die beide eine eigene Domain voraussetzen: Let's Encrypt via HTTP verlangt, dass Port 80 deines Servers aus dem Internet erreichbar ist, also eine Portfreigabe im Router. Let's Encrypt via DNS kommt ohne Freigabe aus und beweist den Besitz der Domain anders: NPM bekommt einen Schlüssel für die Schnittstelle deines DNS-Anbieters und setzt dort kurz selbst einen Eintrag, den Let's Encrypt nachschlägt. NPM bringt dafür eine lange Liste an Anbietern mit. Für zu Hause ist der DNS-Weg der richtige, weil er keinen Port nach draußen öffnet. Aber er ist ein eigener Beitrag, mit Domain kaufen, Schlüssel beim Anbieter holen und einem Wildcard-Zertifikat, das alle Namen unter deiner Domain auf einmal abdeckt. Der kommt.
Was am Ende eingestellt ist
Der Proxy läuft, der erste Name antwortet. Vier Dinge würde ich jetzt noch erledigen, bevor der zweite Host kommt.
1. Zwei Faktoren für das Admin-Konto. Wer in NPM angemeldet ist, kann jeden Dienst hinter dem Proxy umleiten. Seit der neuen Oberfläche gibt es dafür eine zweite Sperre, und sie kostet zwei Minuten:
Oben rechts das Benutzermenü öffnen, darin Two-Factor Auth. Im Dialog Enable Two-Factor Authentication, dann den QR-Code mit deiner Authenticator-App scannen oder den Secret Key von Hand eintragen. Den sechsstelligen Code der App bei Enter verification code eingeben und auf Verify and Enable. NPM zeigt Backup-Codes mit dem Hinweis, sie an einem sicheren Ort abzulegen; jeder gilt nur einmal. In den Passwort-Manager damit, dann I have saved my backup codes. Stand Nginx Proxy Manager 2.15.


2. Port 81 bleibt im Haus. Die Verwaltungsoberfläche läuft über HTTP ohne Verschlüsselung. Im Router darf es keine Freigabe für 81 geben, heute nicht und auch dann nicht, wenn du später 80 und 443 für den Zugriff von außen öffnest. Wer NPM unterwegs verwalten will, nimmt ein VPN.
3. Updates von Hand, mit Blick in die Notes. Wegen der festen Versionsnummer aus Schritt 1 passiert ohne dich nichts. Ein Update besteht aus drei Handgriffen:
Auf der Release-Seite von NPM nachsehen, was die neue Version ändert. Dann die Nummer in der Datei ersetzen:
cd ~/nginx-proxy-manager
nano docker-compose.ymlIn der Zeile image: die 2.15.1 durch die neue Nummer ersetzen, mit Strg+O und Enter speichern, mit Strg+X schließen. Dann ziehen und neu starten:
docker compose pull && docker compose up -d
docker compose psSteht bei STATUS wieder Up und antwortet http://kuma.home.arpa weiterhin, ist das Update durch.
Warum der Blick in die Notes: 2.13.0 hat die komplette Oberfläche ausgetauscht, 2.14.0 hat 32-Bit-ARM fallen lassen. Wer das auf einem alten Pi per latest mitgenommen hätte, hätte einen Container, der nicht mehr startet, und alle Dienste dahinter gleich mit.
4. Der Ordner data gehört ins Backup. Darin liegt die Datenbank mit jedem Host, jedem Benutzer und jeder Zugriffsliste; letsencrypt kommt dazu, sobald Zertifikate drin liegen. Ohne diese beiden Ordner steht nach einem Plattenschaden ein leeres NPM, und du tippst alle Hosts neu. Der Container schreibt sie als root, wie die meisten Images, deshalb sudo beim Kopieren. Die Doku kennt PUID und PGID, um das zu ändern, warnt aber, dass der Container dann Port 80 möglicherweise nicht öffnen darf; für den Anfang lasse ich es.
Wo es klemmt
„502 Bad Gateway“ mit dem Absender openresty. So heißt der Nginx-Unterbau von NPM, die Meldung kommt also vom Proxy selbst: Er hat den Namen erkannt, kommt aber nicht an den Dienst dahinter. Fast immer steht im Feld Forward Hostname / IP etwas, das aus Sicht des Containers nicht existiert: localhost, 127.0.0.1 oder ein Container-Name aus einem Docker-Netz, in dem NPM nicht ist. Ein Issue vom 09-06-2025 beschreibt genau das: Der Proxy erreichte den Container filebrowser nur über seine IP, nicht über den Namen, weil die beiden in verschiedenen Netzen hingen. Die Lösung für den Anfang ist die IP des Rechners plus Port, so wie in Schritt 4. Wer Container-Namen nutzen will, legt ein gemeinsames Docker-Netz an; die Doku beschreibt das unter „Advanced Configuration“, und es ist der Stoff für einen späteren Beitrag.
Der Container startet in Schleife, im Log steht „Address family not supported by protocol“. Auf dem Rechner ist IPv6 abgeschaltet, und Nginx versucht trotzdem, darauf zu lauschen. Die Doku hat dafür eine Zeile im Abschnitt environment der Compose-Datei:
In docker-compose.yml unter TZ ergänzen, gleiche Einrückung:
DISABLE_IPV6: 'true'Dann neu starten:
docker compose up -dDer Name führt zum Docker-Rechner, aber der Browser zeigt „Verbindung abgelehnt“. Dann ist der Name da, aber niemand lauscht auf Port 80, also läuft NPM nicht oder Port 80 ist an einen anderen Dienst vergeben. docker compose ps und der ss-Befehl aus „Was du brauchst“ klären, welches von beiden.
Login mit admin@example.com klappt nicht, oder es kommt eine Anmeldeseite statt des Setup-Dialogs. Das Standardkonto gibt es seit 2.13.0 nicht mehr, egal was die Anleitung sagt, aus der es stammt. Das Konto ist das aus Schritt 2. Erscheint beim allerersten Aufruf schon eine Anmeldeseite, liegt in data eine Datenbank aus einem früheren Versuch, in der schon ein Benutzer steht. Ist das Passwort dazu weg, hilft nur ein Neuanfang, und der löscht alles, was NPM bis dahin wusste, also nur, wenn da wirklich noch nichts ist:
cd ~/nginx-proxy-manager
docker compose down
sudo rm -rf data
docker compose up -dDanach beginnt Schritt 2 von vorn, mit dem Setup-Dialog.
Ein Container, ein Konto, ein Name. Der Rest ist Wiederholung, und Wiederholung ist hier angenehm: Ein neuer Dienst braucht zwei Formulare und keinen Neustart, und niemand in deinem Haus muss mehr wissen, was 8096 bedeutet.
Wie viele Port-Nummern hast du gerade im Kopf, und welcher Dienst bekommt bei dir als Erstes einen Namen? Schreib es in die Kommentare, samt der Endung, die du gewählt hast; mich interessiert, ob home.arpa sich durchsetzt oder alle bei .lan bleiben.
Falls Pi-hole bei dir noch fehlt und Schritt 3 deshalb an der hosts-Datei hängen blieb: Pi-hole per Docker Compose steht in einer Stunde, und danach kennt jedes Gerät im Haus deine Namen.
Quellen
- Nginx Proxy Manager: Full Setup Instructions (Compose-Beispiel, Ports, Volumes, DB_SQLITE_FILE, Initial Run, ARM-Hinweis zu 2.14)
- Nginx Proxy Manager: Guide (Features, Quick Setup, Port 81, MIT-Lizenz)
- Nginx Proxy Manager: Advanced Configuration (Docker-Netz als Best Practice, DISABLE_IPV6, PUID/PGID und Port 80)
- Nginx Proxy Manager: FAQ (Docker als einzige unterstützte Installation, Raspberry Pi)
- Nginx Proxy Manager: Release v2.15.1 (03-06-2026)
- Nginx Proxy Manager: Release v2.13.0 (04-11-2025, neue Oberfläche, Setup-Assistent statt Standard-Benutzer)
- Nginx Proxy Manager: backend/setup.js (INITIAL_ADMIN_EMAIL, INITIAL_ADMIN_PASSWORD, Setup nur bei leerer Benutzertabelle)
- Nginx Proxy Manager: Setup-Seite (Felder Name, E-Mail, Passwort mit 8 bis 100 Zeichen, automatische Anmeldung)
- Nginx Proxy Manager: Texte der Oberfläche (Welcome, Dashboard-Kacheln, Felder des Proxy Hosts, SSL-Optionen, 2FA-Dialog)
- Nginx Proxy Manager: SiteHeader.tsx (Benutzermenü mit Edit Profile, Change Password, Two-Factor Auth, Logout)
- Nginx Proxy Manager: Issue #4572, 502 Bad Gateway mit Container-Namen (09-06-2025)
- Docker Hub: jc21/nginx-proxy-manager, Tags latest, 2 und 2.15.1
- Uptime Kuma Wiki: Reverse Proxy (WebSockets Support für Nginx Proxy Manager)
- Pi-hole Web-Interface: settings-dnsrecords.lp (Local DNS records, Felder Domain und IP)
- RFC 8375: Special-Use Domain 'home.arpa.' (für Heimnetze reserviert, keine Weiterleitung ins Internet)
- RFC 6762: Multicast DNS (Anfragen auf .local gehen per Multicast, nicht an den DNS-Server)
- Nginx Proxy Manager: ProxyHosts/Table.tsx (Spalten der Host-Liste, Status Online/Offline)
- Nginx Proxy Manager: block-exploits.conf (was Block Common Exploits abweist)