🐳 Docker

Sicherer Reverse Proxy mit Nginx Proxy Manager und Docker

Sicherer Reverse Proxy mit Nginx Proxy Manager und Docker
⚠️ Hinweis: Alle Guides auf smoth.me dienen ausschließlich zu Informations- und Lernzwecken. Die Umsetzung erfolgt auf eigene Gefahr. Wir übernehmen keine Haftung für Schäden, Datenverluste oder Systemausfälle, die durch die Anwendung dieser Anleitungen entstehen können. → Vollständiger Haftungsausschluss

Na, alter Hase oder motivierter Neuling? Egal, wo du stehst, willkommen bei smoth.me! Heute nehmen wir uns ein Thema vor, das in jedem Heimlabor, das etwas auf sich hält, eine zentrale Rolle spielt: Den sicheren Zugriff auf deine Dienste von außen. Und nein, ich rede nicht davon, Ports wild aufzureißen. Wir reden über einen Reverse Proxy, genauer gesagt über den Nginx Proxy Manager (NPM), verpackt in Docker-Containern. In meiner Erfahrung ist das eine der elegantesten und benutzerfreundlichsten Lösungen, um deine Home Assistant, N8N, Nextcloud oder was auch immer du sonst so laufen hast, sicher mit SSL-Zertifikaten (dank Let's Encrypt) von außen erreichbar zu machen.

Warum ein Reverse Proxy? Ganz einfach: Er ist dein Türsteher. Anstatt dass jeder deiner Dienste direkt ins Internet lauscht, schickst du den gesamten Traffic über eine einzige, kontrollierte Instanz. Das erhöht die Sicherheit, vereinfacht die Zertifikatsverwaltung und ermöglicht es dir, mehrere Dienste unter verschiedenen Subdomains (z.B. homeassistant.deinedomain.de, n8n.deinedomain.de) über denselben Port (meist 443 für HTTPS) zu erreichen. Wer das zum ersten Mal einrichtet, stolpert oft über DNS-Einstellungen oder Netzwerkprobleme, aber keine Sorge, ich führe dich da durch.

Voraussetzungen – Was du mitbringen solltest

Bevor wir loslegen, lass uns kurz checken, ob du alles am Start hast. Die meisten dieser Punkte sind Standard in einem gut geführten Heimlabor:

  • Ein Linux-Server: Das kann ein Raspberry Pi, ein alter PC, eine VM auf Proxmox oder ein dedizierter Server sein. Hauptsache, er läuft stabil und hat genügend Ressourcen (mindestens 1GB RAM, 2 CPU-Kerne für eine reibungslose NPM-Erfahrung, mehr, wenn er noch andere Dienste hostet).
  • Docker und Docker Compose: Das ist unsere Basis. Falls du das noch nicht installiert hast, findest du unzählige Guides dazu. Ich gehe davon aus, dass du `docker` und `docker compose` (oder `docker-compose` je nach Version) bereits auf deinem System hast.
  • Eine eigene Domain: Zum Beispiel deinedomain.de. Ohne eine eigene Domain wird es mit Let's Encrypt schwierig und ist auch nicht der Sinn der Sache.
  • Zugriff auf die DNS-Einstellungen deiner Domain: Du musst A-Records (oder AAAA-Records für IPv6) für deine Subdomains auf die öffentliche IP-Adresse deines Servers zeigen lassen können.
  • Geöffnete Ports im Router/Firewall: Die Ports 80 (HTTP) und 443 (HTTPS) müssen von außen auf die IP-Adresse deines Servers weitergeleitet werden. Wichtig: Nur diese beiden Ports! Alles andere bleibt intern.
  • Grundkenntnisse in der Shell: Wir werden ein paar Befehle eintippen, aber keine Sorge, es ist nichts Kompliziertes.

Schritt-für-Schritt-Anleitung: Nginx Proxy Manager einrichten

Schritt 1: Docker Compose vorbereiten

Zuerst erstellen wir ein Verzeichnis für unsere NPM-Konfiguration. Ich mache das gerne unter /opt/docker, aber du kannst auch einen anderen Pfad wählen. Wichtig ist, dass du deine Konfigurationsdateien nicht verlierst, falls du mal den Server neu aufsetzen musst. Also, legen wir los:

sudo mkdir -p /opt/docker/nginx-proxy-manager
cd /opt/docker/nginx-proxy-manager

Als Nächstes erstellen wir unsere docker-compose.yml Datei. Diese Datei definiert alle Dienste, die wir für Nginx Proxy Manager benötigen:

nano docker-compose.yml

Füge den folgenden Inhalt ein. Ich erkläre dir gleich, was die einzelnen Zeilen bedeuten:

version: '3.8'

services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
      - '81:81'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    environment:
      DB_MYSQL_HOST: "db"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm"
      DB_MYSQL_PASSWORD: "npm_password" # ERSETZE DIESES PASSWORT!
      DB_MYSQL_NAME: "npm"
    depends_on:
      - db
    networks:
      - default # Für NPM selbst
      - proxy-network # Für die Kommunikation mit den Backends

  db:
    image: 'mariadb:10.6' # Oder postgres:13
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "npm_root_password" # ERSETZE DIESES PASSWORT!
      MYSQL_DATABASE: "npm"
      MYSQL_USER: "npm"
      MYSQL_PASSWORD: "npm_password" # ERSETZE DIESES PASSWORT!
    volumes:
      - ./data/mysql:/var/lib/mysql
    networks:
      - default

networks:
  proxy-network:
    external: true
  default:
    # Hier keine besonderen Einstellungen, Docker kümmert sich um die Erstellung

Kurze Erklärung zur docker-compose.yml:

  • services: Hier definieren wir unsere Container.
    • app: Das ist der Nginx Proxy Manager selbst.
      • image: Wir nutzen das offizielle Docker-Image.
      • restart: unless-stopped: Sorgt dafür, dass der Container nach einem Neustart des Servers oder einem Absturz automatisch wieder startet.
      • ports:
        • 80:80 und 443:443: Das sind die Ports für HTTP und HTTPS, die NPM für deine Proxy-Dienste nutzt. Diese müssen auf deinem Server verfügbar sein und vom Router weitergeleitet werden.
        • 81:81: Das ist der Port für die Admin-Oberfläche von Nginx Proxy Manager. Ich empfehle, diesen Port nicht direkt ins Internet zu öffnen, sondern nur lokal zugänglich zu machen oder über einen VPN zu nutzen. Wenn du ihn trotzdem von außen erreichbar machen willst, musst du ihn natürlich auch im Router weiterleiten.
      • volumes: Hier persistieren wir die Daten.
        • ./data:/data: Enthält die NPM-Konfiguration und interne Datenbank-Dateien.
        • ./letsencrypt:/etc/letsencrypt: Hier werden die Let's Encrypt Zertifikate gespeichert. Extrem wichtig, dass diese persistent sind!
      • environment: Hier konfigurieren wir die Datenbank-Verbindung.
      • depends_on: - db: Stellt sicher, dass die Datenbank vor NPM startet.
      • networks: Hier definieren wir, mit welchen Netzwerken der Container verbunden ist.
        • default: Das Standard-Netzwerk, das Docker Compose für diese Dienste erstellt.
        • proxy-network: Das ist mein Tipp! Ein separates, externes Docker-Netzwerk. In dieses Netzwerk fügst du später all deine anderen Dienste hinzu (Home Assistant, N8N, etc.), die du über NPM proxyn möchtest. So können sie sicher miteinander kommunizieren, ohne dass du Ports in jedem Container einzeln mappen musst.
    • db: Der Datenbank-Container für NPM. Ich nutze hier MariaDB, da es leichtgewichtig und stabil ist.
  • networks: Hier definieren wir unsere Docker-Netzwerke.
    • proxy-network: external: true: Bedeutet, dieses Netzwerk wird nicht von diesem docker-compose.yml erstellt, sondern muss bereits existieren. Das ist wichtig, damit du es später auch für andere docker-compose.yml Dateien nutzen kannst.
    • default: Wird automatisch erstellt.

Ganz wichtig: Ändere die Passwörter! Die Platzhalter npm_password und npm_root_password sind unsicher und müssen durch sichere, einzigartige Passwörter ersetzt werden.

Schritt 2: Das externe Docker-Netzwerk erstellen

Da wir ein externes Netzwerk namens proxy-network in unserer docker-compose.yml definiert haben, müssen wir es jetzt erstellen, falls es noch nicht existiert. Das ist ein einmaliger Schritt:

docker network create proxy-network

Wenn du das Netzwerk schon hast (weil du vielleicht schon andere Dienste so betreibst), wird Docker dir einfach sagen, dass es bereits existiert. Kein Problem.

Schritt 3: Nginx Proxy Manager starten

Jetzt wird's spannend! Navigiere in das Verzeichnis, in dem deine docker-compose.yml liegt (/opt/docker/nginx-proxy-manager) und starte die Dienste:

docker compose up -d

Der Parameter -d sorgt dafür, dass die Container im Hintergrund laufen (detached mode). Es kann ein paar Minuten dauern, bis alle Images heruntergeladen und die Container gestartet sind.

Du kannst den Status überprüfen mit:

docker compose ps

Beide Container (app und db) sollten den Status running haben.

Schritt 4: Erste Anmeldung und Einrichtung

Öffne deinen Webbrowser und navigiere zur Admin-Oberfläche von Nginx Proxy Manager. Wenn du den Port 81 nicht nach außen geöffnet hast, musst du das von einem Gerät im selben Netzwerk wie dein Server tun. Die Adresse ist dann:

http://<IP_DEINES_SERVERS>:81

Beim ersten Login sind die Standard-Anmeldedaten:

  • Email: admin@example.com
  • Password: changeme

Nach dem ersten Login wirst du aufgefordert, diese Daten zu ändern. Mach das sofort! Wähle ein starkes Passwort und eine gültige E-Mail-Adresse.

Schritt 5: Einen Proxy Host hinzufügen

Jetzt kommt der eigentliche Spaß! Wir fügen einen Proxy Host für einen deiner Dienste hinzu. Nehmen wir an, du hast Home Assistant auf Port 8123 laufen und möchtest es unter homeassistant.deinedomain.de erreichen.

  1. Navigiere im NPM-Dashboard zu "Hosts" -> "Proxy Hosts".
  2. Klicke auf "Add Proxy Host".
  3. Details Tab:
    • Domain Names: Gib hier deine gewünschte Subdomain ein, z.B. homeassistant.deinedomain.de.
    • Scheme: Meist http, da die SSL-Verschlüsselung von NPM übernommen wird.
    • Forward Hostname / IP: Hier gibst du die interne IP-Adresse oder den Docker-Containernamen deines Home Assistant-Dienstes an. Wenn Home Assistant im selben proxy-network läuft, kannst du einfach den Containernamen verwenden (z.B. homeassistant). Andernfalls die IP-Adresse des Servers, auf dem HA läuft.
    • Forward Port: Der interne Port deines Dienstes, z.B. 8123 für Home Assistant.
    • Aktiviere "Block Common Exploits" und "Websockets Support" (letzteres ist wichtig für viele Dienste wie Home Assistant).
  4. SSL Tab:
    • Wähle "Request a new SSL Certificate".
    • Aktiviere "Force SSL".
    • Gib deine E-Mail-Adresse für Let's Encrypt Benachrichtigungen ein.
    • Aktiviere "I Agree to the Let's Encrypt Terms of Service".
    • Klicke auf "Save".

Wichtig zu wissen: Bevor Let's Encrypt dein Zertifikat ausstellen kann, muss deine Domain (homeassistant.deinedomain.de) auf die öffentliche IP-Adresse deines Servers zeigen. Du musst also in den DNS-Einstellungen deines Domain-Anbieters einen A-Record für homeassistant erstellen, der auf die IP deines Servers verweist. Wenn das nicht korrekt ist, schlägt die Zertifikatsanfrage fehl!

Schritt 6: Wildcard-Zertifikate (Mein Tipp für Fortgeschrittene)

Wenn du viele Subdomains hast (homeassistant.deinedomain.de, n8n.deinedomain.de, grafana.deinedomain.de etc.), ist es mühsam, für jede ein eigenes Zertifikat anzufordern. Hier kommen Wildcard-Zertifikate ins Spiel (z.B. *.deinedomain.de). Ein einziges Zertifikat deckt alle deine Subdomains ab!

Das Anfordern eines Wildcard-Zertifikats erfordert eine DNS-Challenge. Das bedeutet, Let's Encrypt überprüft, ob du die Kontrolle über die Domain hast, indem du einen speziellen TXT-Eintrag in deine DNS-Zone einfügst. NPM kann das automatisch für dich machen, wenn du einen DNS-Provider hast, der von Certbot (der Engine hinter Let's Encrypt) unterstützt wird und du NPM die API-Zugangsdaten gibst (z.B. für Cloudflare).

  1. Gehe im NPM-Dashboard zu "SSL Certificates".
  2. Klicke auf "Add SSL Certificate" -> "Let's Encrypt".
  3. Gib deine Wildcard-Domain ein, z.B. *.deinedomain.de und zusätzlich die Root-Domain deinedomain.de (wichtig!).
  4. Aktiviere "Use DNS Challenge".
  5. Wähle deinen DNS-Provider aus der Liste (z.B. cloudflare).
  6. Füge die erforderlichen API-Credentials hinzu (z.B. Cloudflare Global API Key und E-Mail). Diese Daten sind sensibel und sollten sicher behandelt werden.
  7. Aktiviere "I Agree to the Let's Encrypt Terms of Service".
  8. Klicke auf "Save".

Wenn alles klappt, hast du ein Wildcard-Zertifikat, das du dann für alle deine Proxy Hosts verwenden kannst. Einfach beim Erstellen oder Bearbeiten eines Proxy Hosts im SSL-Tab das neue Wildcard-Zertifikat auswählen.

Häufige Fehler und Lösungen

In meiner Erfahrung stolpert man über ein paar typische Probleme. Hier sind die gängigsten und wie du sie löst:

Problem 1: DNS-Auflösungsprobleme – "Domain does not point to this server"

Wenn du versuchst, ein Let's Encrypt Zertifikat anzufordern und NPM meldet, dass die Domain nicht auf deinen Server zeigt, ist das fast immer ein DNS-Problem.

  • Ursache: Der A-Record (oder AAAA-Record für IPv6) deiner Subdomain ist entweder nicht vorhanden, falsch geschrieben oder zeigt auf die falsche IP-Adresse. Let's Encrypt überprüft die öffentliche IP, von der die Anfrage kommt, und vergleicht sie mit dem, was DNS für deine Domain meldet.
  • Lösung:
    1. Überprüfe im DNS-Management deines Domain-Anbieters, ob ein A-Record für homeassistant.deinedomain.de (oder welche Subdomain du auch immer nutzt) existiert.
    2. Stelle sicher, dass dieser A-Record auf die öffentliche IP-Adresse deines Servers zeigt, nicht auf eine interne LAN-IP.
    3. Beachte die TTL (Time To Live) des DNS-Eintrags. Es kann bis zu 24 Stunden dauern, bis Änderungen global propagiert werden, auch wenn es oft schneller geht. Nutze Tools wie dig oder nslookup auf einem externen System, um die Auflösung zu testen:
      dig +short homeassistant.deinedomain.de
      Der Befehl sollte die öffentliche IP deines Servers zurückgeben.

Problem 2: SSL-Zertifikatfehler – "Connection timed out" oder "Failed to connect to Let's Encrypt"

Wenn NPM keine Verbindung zu den Let's Encrypt Servern herstellen kann, um ein Zertifikat anzufordern.

  • Ursache:
    • Die Ports 80 und/oder 443 sind auf deinem Router/Firewall nicht korrekt auf deinen Server weitergeleitet.
    • Eine Firewall auf deinem Server blockiert ausgehenden oder eingehenden Traffic auf diesen Ports.
    • Let's Encrypt Rate Limits wurden erreicht (unwahrscheinlich, aber möglich bei vielen Fehlversuchen).
  • Lösung:
    1. Router-Konfiguration: Überprüfe deine Port-Weiterleitungsregeln (Port Forwarding, NAT) im Router. Port 80 und 443 TCP müssen von außen auf die interne IP-Adresse deines Servers geleitet werden.
    2. Server-Firewall: Wenn du eine Software-Firewall wie UFW auf deinem Server nutzt, stelle sicher, dass die Ports 80, 443 und 81 (für die Admin-Oberfläche) erlaubt sind:
      sudo ufw allow 80/tcp
      sudo ufw allow 443/tcp
      sudo ufw allow 81/tcp # Nur falls du die Admin-Oberfläche von außen brauchst
      sudo ufw reload
    3. Let's Encrypt Rate Limits: Wenn du zu viele Zertifikate für dieselbe Domain in kurzer Zeit angefordert hast, kann es sein, dass Let's Encrypt dich temporär blockiert. Warte in diesem Fall ein paar Stunden. Für Tests solltest du die Staging-Umgebung von Let's Encrypt nutzen (Option in NPM unter "SSL Certificates" -> "Let's Encrypt" -> "Use Staging").

Problem 3: "502 Bad Gateway" oder "Connection Refused"

Diese Fehler treten auf, wenn NPM den Traffic zwar empfängt, aber die Verbindung zu deinem internen Dienst (z.B. Home Assistant) nicht herstellen kann.

  • Ursache:
    • Der interne Dienst läuft nicht oder ist nicht erreichbar.
    • Falscher "Forward Hostname / IP" oder "Forward Port" im Proxy Host von NPM.
    • Netzwerkprobleme zwischen NPM und dem Backend-Dienst (z.B. der Backend-Dienst ist nicht im proxy-network).
  • Lösung:
    1. Dienststatus prüfen: Stelle sicher, dass dein Backend-Dienst (z.B. Home Assistant) läuft und intern erreichbar ist. Versuch mal, ihn direkt über seine interne IP und Port aufzurufen (z.B. http://<interne_HA_IP>:8123).
    2. NPM-Konfiguration prüfen: Geh in NPM zu deinem Proxy Host und überprüfe den "Forward Hostname / IP" und "Forward Port". Sind sie korrekt?
      • Wenn dein Backend-Dienst ein Docker-Container ist und im selben proxy-network wie NPM läuft, kannst du den Containernamen als "Forward Hostname" verwenden (z.B. homeassistant). Das ist die sauberste Lösung.
      • Läuft der Dienst auf einem anderen Server oder direkt auf dem Host, gib die interne IP-Adresse des Servers an.
    3. Docker-Netzwerk: Wenn du das proxy-network nutzt, stelle sicher, dass dein Backend-Dienst auch in diesem Netzwerk ist. Wenn du z.B. Home Assistant mit Docker Compose betreibst, sollte deine docker-compose.yml für Home Assistant ungefähr so aussehen:
      services:
        homeassistant:
          image: ghcr.io/home-assistant/home-assistant:stable
          restart: unless-stopped
          ports:
            - 8123:8123 # Diesen Port nur mappen, wenn du HA auch intern direkt erreichen willst
          volumes:
            - ./config:/config
          environment:
            TZ: Europe/Berlin
          networks:
            - proxy-network # HIER IST DER WICHTIGE PUNKT!
      
      networks:
        proxy-network:
          external: true

Fazit und nächste Schritte

Herzlichen Glückwunsch! Du hast jetzt einen voll funktionsfähigen und sicheren Reverse Proxy mit Nginx Proxy Manager auf deinem Server am Laufen. Das ist ein riesiger Schritt für jedes Heimlabor, da es dir nicht nur Sicherheit und Komfort bietet, sondern auch die Basis für viele weitere spannende Projekte legt.

Was kommt als Nächstes?

  • Absicherung der Admin-Oberfläche: Überlege, den Port 81 nicht nach außen zu öffnen und stattdessen nur über einen VPN-Zugang auf die NPM-Oberfläche zuzugreifen. Das ist der sicherste Weg.
  • Authentifizierung vor dem Proxy: Für noch mehr Sicherheit kannst du Dienste wie Authelia oder Authentik vor NPM schalten, um eine 2-Faktor-Authentifizierung für deine Dienste zu erzwingen, selbst wenn der Dienst selbst keine hat.
  • Caching und erweiterte Nginx-Konfigurationen: NPM bietet auch die Möglichkeit, benutzerdefinierte Nginx-Konfigurationen einzufügen, um z.B. Caching zu aktivieren oder spezielle Header zu setzen. Das ist dann aber schon für die ganz Fortgeschrittenen unter euch.

Ich hoffe, dieser Guide hat dir geholfen, deine Dienste sicher und elegant ins Netz zu bringen. Viel Spaß beim Experimentieren in deinem Heimlabor!

Weitere Guides aus "Docker"

Helm im Homelab: Kubernetes-Anwendungen effizient verwalten
Lerne, wie du Kubernetes-Applikationen mit Helm in deinem Homelab oder als Admin effizient verwaltes…
K3s im Homelab: Dein erster Kubernetes-Cluster mit Proxmox
Erfahre, wie du einen schlanken K3s Kubernetes-Cluster in deinem Homelab auf Proxmox-VMs aufsetzt. E…
LGTM im Homelab: Observability mit Grafana, Loki, Tempo, Mimir
Lerne, wie du mit Grafana, Loki, Tempo und Mimir (LGTM) deinen Homelab-Betrieb überwachst, Logs zent…
Super Productivity 18.0 im Homelab mit Docker
Ich zeige dir, wie du Super Productivity 18.0 in deinem Homelab mit Docker betreibst und seine neuen…