Robuster Docker Compose Stack mit Nginx Proxy Manager
Einleitung: Dein Homelab auf Steroiden mit Docker und NPM
Moin, liebe smoth.me Community! Als alter Hase im Homelab-Dschungel habe ich über die Jahre unzählige Stunden damit verbracht, Dienste zu deployen, zu optimieren und vor allem – sie sicher und zuverlässig erreichbar zu machen. Wer kennt es nicht? Man hat ein Dutzend Dienste auf verschiedenen Ports laufen und verliert irgendwann den Überblick. Oder man will endlich mal HTTPS für seine internen Tools, ohne sich jedes Mal mit obskuren Zertifikats-Bastelleien herumschlagen zu müssen.
Genau hier setzt der heutige Guide an. Inspiriert von den "Developer-Häppchen", die oft kleine, aber feine Optimierungen oder Tools vorstellen, möchte ich dir heute zeigen, wie du einen robusten Docker Compose Stack mit Nginx Proxy Manager (NPM) aufsetzt. In meiner Erfahrung ist das die eleganteste Lösung, um deine Docker-Dienste im Homelab zu verwalten, sie über eine einzige IP-Adresse nach außen zu bringen und gleichzeitig ganz unkompliziert Let's Encrypt Zertifikate dafür zu bekommen. Das ist die Basis, auf der bei mir fast alles läuft – von Home Assistant über n8n bis hin zu kleinen Tools, die ich mal eben schnell teste.
Wir bauen heute eine solide Grundlage: Ein dediziertes Docker-Netzwerk, in dem dein NPM läuft und als zentrale Anlaufstelle für alle anderen Dienste dient. Dann binden wir eine Beispielanwendung ein und richten sie im NPM ein, inklusive SSL. Das Schöne daran: Einmal aufgesetzt, kannst du beliebig viele weitere Docker-Dienste hinzufügen, ohne dich wieder um Ports oder Zertifikate kümmern zu müssen. Das ist wirklich ein Game Changer für die Produktivität im Homelab!
Voraussetzungen: Was du mitbringen solltest
Bevor wir loslegen, lass uns kurz klären, was du für dieses Setup brauchst. Keine Sorge, es ist nichts Spektakuläres:
- Ein Linux-System: Ich persönlich setze am liebsten auf eine Debian- oder Ubuntu-VM oder einen LXC in Proxmox. Das ist stabil und gut dokumentiert. Ein Raspberry Pi oder ein anderer Mini-PC tut es aber auch.
- Grundkenntnisse in der Linux-Kommandozeile: Du solltest dich auf der Shell bewegen und grundlegende Befehle wie
cd,ls,mkdirundnanobeherrschen. Wer das zum ersten Mal macht, stolpert oft über Dateipfade – sei hier einfach präzise. - SSH-Zugriff: Ein Muss, um komfortabel arbeiten zu können.
- Docker und Docker Compose: Die Basis unseres Setups. Ich gehe davon aus, dass du Docker bereits installiert hast. Falls nicht, zeige ich dir kurz, wie es geht.
- Optional, aber dringend empfohlen für externen Zugriff:
- Eine eigene Domain oder Subdomain: Für Let's Encrypt Zertifikate und einen professionellen Auftritt unerlässlich.
- Port-Weiterleitung (Port Forwarding) in deinem Router: Die Ports 80 (HTTP) und 443 (HTTPS) müssen von deinem Router auf die IP-Adresse deines Linux-Servers weitergeleitet werden, damit Let's Encrypt die Zertifikate ausstellen und deine Dienste von außen erreichbar sind. Mein Tipp: Wenn du keine feste IP hast, nutze einen Dynamic DNS (DynDNS) Dienst.
Schritt 1: System-Vorbereitung und Docker-Installation
Zuerst bringen wir dein System auf den neuesten Stand und stellen sicher, dass Docker und Docker Compose bereit sind. Wenn du Docker schon drauf hast, kannst du diesen Schritt überspringen und direkt zu den Tests springen.
Logge dich per SSH auf deinem Server ein:
ssh deinuser@deine-server-ip
System aktualisieren:
sudo apt update && sudo apt upgrade -y
Sollte Docker noch nicht installiert sein, hier die gängige Methode für Debian/Ubuntu. Ich nutze hier gerne das offizielle Installationsskript, da es die neuesten Versionen bereitstellt:
# Docker-Installationsskript herunterladen und ausführen
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Docker Compose V2 installieren (meist schon mit dem Skript dabei oder als Plugin)
# Falls docker compose nicht funktioniert, versuche:
# sudo apt install docker-compose-plugin
# Oder lade es manuell herunter, falls du eine ältere Distribution nutzt
# sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# sudo chmod +x /usr/local/bin/docker-compose
# Benutzer zur Docker-Gruppe hinzufügen, damit du nicht immer sudo nutzen musst
sudo usermod -aG docker $USER
# Achtung: Du musst dich einmal abmelden und wieder anmelden, damit die Gruppenänderung wirksam wird!
# Oder führe 'newgrp docker' aus, um die Gruppe für die aktuelle Session zu aktivieren.
Nachdem du dich neu angemeldet hast (oder newgrp docker ausgeführt hast), testen wir, ob Docker korrekt läuft:
docker run hello-world
Wenn du eine Begrüßungsnachricht siehst, ist alles paletti!
Schritt 2: Das Herzstück – Dein Docker Compose Netzwerk
Einer der wichtigsten Tipps, die ich dir geben kann, wenn du mit Docker Compose arbeitest, ist: Nutze dedizierte Netzwerke! Warum? Es sorgt für Isolation, bessere Übersicht und vor allem für eine einfache Namensauflösung zwischen deinen Containern. Statt IPs zu nutzen, sprichst du Container einfach mit ihrem Servicenamen an – das macht Konfigurationen viel sauberer.
Wir erstellen ein Bridge-Netzwerk, das speziell für unsere Proxy-Dienste und die dahinterliegenden Anwendungen gedacht ist. Ich nenne es gerne proxy-network:
docker network create proxy-network
Das war's schon! Dieses Netzwerk werden wir gleich in unseren docker-compose.yml Dateien referenzieren.
Schritt 3: Nginx Proxy Manager (NPM) im Docker Compose Stack
Der Nginx Proxy Manager (NPM) ist ein absoluter Segen für Homelab-Enthusiasten. Er bietet eine schicke Weboberfläche, um Reverse-Proxy-Hosts zu konfigurieren, SSL-Zertifikate von Let's Encrypt zu beantragen und sogar grundlegende Authentifizierung zu verwalten. Kein manuelles Nginx-Konfigurieren mehr!
Zuerst erstellen wir einen Ordner für unsere NPM-Konfigurationen und Datenbankdaten. Ich lege meine Docker-Stacks gerne in ~/docker/ ab:
mkdir -p ~/docker/nginx-proxy-manager
Wechsle in diesen Ordner und erstelle eine docker-compose.yml-Datei:
cd ~/docker/nginx-proxy-manager
nano docker-compose.yml
Füge den folgenden Inhalt ein. Achte darauf, dass die Volumes auf deine lokalen Pfade zeigen und das Netzwerk korrekt benannt ist:
version: '3.8'
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
# Diese Ports müssen auf deinem Host frei sein und sind für den Zugriff von außen
# Port 80 für HTTP-Anfragen und Let's Encrypt Challenge
- '80:80'
# Port 443 für HTTPS-Anfragen
- '443:443'
# Port 81 für das Nginx Proxy Manager Webinterface
- '81:81'
environment:
DB_MYSQL_HOST: 'db'
DB_MYSQL_PORT: 3306
DB_MYSQL_USER: 'npm'
DB_MYSQL_PASSWORD: 'npm_password_sicher_aendern' # Wichtig: Ändere dies!
DB_MYSQL_NAME: 'npm'
# Optional: Wenn du SQLite statt MySQL nutzen möchtest (einfacher für kleine Setups)
# DB_SQLITE_FILE: '/data/database.sqlite'
volumes:
# Hier werden die Konfigurationsdateien und Zertifikate gespeichert
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- proxy-network # Wichtig: Unser dediziertes Netzwerk
db:
image: 'mariadb:10.6' # Oder postgres:13, wenn du das bevorzugst
container_name: npm_database
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: 'root_password_sicher_aendern' # Wichtig: Ändere dies!
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm_password_sicher_aendern' # Muss mit NPM-Konfiguration übereinstimmen
volumes:
# Hier werden die Datenbankdaten gespeichert
- ./mysql:/var/lib/mysql
networks:
- proxy-network # Wichtig: Im selben Netzwerk wie NPM
networks:
proxy-network:
external: true # Sagt Docker, dass dieses Netzwerk bereits existiert
Wichtig zu wissen: Die Passwörter für die Datenbank npm_password_sicher_aendern und root_password_sicher_aendern musst du unbedingt ändern! Nutze sichere, zufällige Passwörter. Ich nutze hier MariaDB, da es ressourcenschonend ist und gut mit NPM harmoniert. Für kleinere Setups könnte auch die SQLite-Option interessant sein, dann brauchst du den db-Service nicht.
Speichere die Datei (Strg+O, Enter, Strg+X) und starte den Stack:
docker compose up -d
Warte einen Moment, bis die Container hochgefahren sind. Du kannst den Status überprüfen mit:
docker compose ps
Wenn alles läuft, solltest du jetzt das Webinterface des Nginx Proxy Managers erreichen können. Öffne deinen Browser und navigiere zu http://deine-server-ip:81.
Der Standard-Login ist:
- E-Mail:
admin@example.com - Passwort:
changeme
Beim ersten Login wirst du aufgefordert, deine Daten zu ändern. Mach das sofort und nutze ein sicheres Passwort!
Schritt 4: Eine Beispiel-Anwendung hinzufügen und via NPM exposen
Jetzt, wo NPM läuft, ist es Zeit, eine erste Anwendung dahinterzulegen. Wir nehmen einen einfachen Nginx-Container, der eine statische HTML-Seite ausliefert. Das demonstriert perfekt, wie das Proxying funktioniert.
Erstelle einen neuen Ordner für unsere Beispiel-App und darin einen html-Ordner für unsere Webseite:
mkdir -p ~/docker/my-web-app/html
Erstelle eine einfache index.html-Datei in diesem Ordner:
echo "Hallo vom Homelab! Willkommen auf meinem Docker-Stack!
" > ~/docker/my-web-app/html/index.html
Navigiere in den Ordner ~/docker/my-web-app und erstelle eine weitere docker-compose.yml:
cd ~/docker/my-web-app
nano docker-compose.yml
Füge diesen Inhalt ein:
version: '3.8'
services:
my-web-app:
image: 'nginx:alpine' # Ein schlanker Nginx-Container
container_name: my-web-app # Wichtig für die Namensauflösung im NPM
restart: unless-stopped
volumes:
- ./html:/usr/share/nginx/html:ro # Unsere HTML-Seite einbinden (read-only)
networks:
- proxy-network # Muss im selben Netzwerk wie NPM sein!
networks:
proxy-network:
external: true # Sagt Docker, dass dieses Netzwerk bereits existiert
Starte auch diesen Stack im Hintergrund:
docker compose up -d
Die App läuft jetzt intern im proxy-network. Um sie erreichbar zu machen, gehen wir zurück zum Nginx Proxy Manager Webinterface.
NPM Konfiguration für die Beispiel-App
- Gehe im NPM-Interface zu "Hosts" -> "Proxy Hosts".
- Klicke auf "Add Proxy Host".
-
Details Tab:
- Domain Names: Gib hier deine (Sub-)Domain ein, z.B.
meine-app.deine-domain.de. - Scheme:
http - Forward Hostname / IP: Hier kommt der
container_namedeines Docker-Dienstes rein, alsomy-web-app. NPM kann diesen Namen im gemeinsamen Docker-Netzwerk auflösen! Das ist der Clou. - Forward Port: Der interne Port des Containers, in unserem Fall
80(Standard-Nginx-Port). - Aktiviere "Block Common Exploits" und "Websockets Support" (oft nützlich).
- Domain Names: Gib hier deine (Sub-)Domain ein, z.B.
-
SSL Tab:
- Wähle "Request a new SSL Certificate".
- Aktiviere "Force SSL".
- Aktiviere "HTTP/2 Support".
- Stimme den Let's Encrypt ToS zu und gib deine E-Mail-Adresse ein.
- Klicke auf "Save".
NPM wird nun versuchen, ein Let's Encrypt Zertifikat für deine Domain zu beantragen. Wenn deine Port-Weiterleitungen und DNS-Einstellungen korrekt sind, sollte dies innerhalb weniger Sekunden erfolgreich sein. Danach kannst du deine Domain (z.B. https://meine-app.deine-domain.de) im Browser aufrufen und solltest deine "Hallo vom Homelab!"-Nachricht sehen – gesichert mit einem gültigen SSL-Zertifikat!
Häufige Fehler und ihre Lösungen
Auch ich stolpere immer wieder über die gleichen Dinge. Hier sind ein paar typische Probleme und wie du sie behebst:
1. Port-Konflikte beim Starten von NPM
Fehlermeldung: Du siehst etwas wie docker: Error response from daemon: driver failed programming external connectivity on endpoint... listen tcp 0.0.0.0:80: bind: address already in use.
Problem: Ein anderer Dienst auf deinem Hostsystem nutzt bereits die Ports 80, 443 oder 81, die NPM belegen möchte. Das kann ein Apache, Nginx oder ein anderer Webserver sein, der direkt auf dem Host läuft.
Lösung: Finde den Prozess, der den Port belegt, und stoppe ihn. Für Port 80 könntest du das tun:
sudo netstat -tulpn | grep :80
Das zeigt dir den Prozess und seine PID (Prozess-ID) an. Dann kannst du den Prozess mit sudo kill -9 PID beenden. Alternativ kannst du die externen Ports von NPM in der docker-compose.yml ändern (z.B. - '8080:80' für HTTP und - '8443:443' für HTTPS), aber das ist weniger ideal, wenn du Let's Encrypt nutzen möchtest, da Let's Encrypt standardmäßig Port 80/443 erwartet.
2. NPM kann Backend-Dienst nicht erreichen (502 Bad Gateway)
Fehlermeldung: Wenn du deine Domain aufrufst, siehst du im Browser einen 502 Bad Gateway Fehler oder eine ähnliche Fehlermeldung von NPM.
Problem: NPM kann den Container deiner Anwendung nicht erreichen. Das liegt meist an einem der folgenden Gründe:
- Falscher "Forward Hostname / IP": Du hast nicht den korrekten Docker-Containernamen verwendet oder eine IP-Adresse, die im Docker-Netzwerk nicht funktioniert.
- Falscher "Forward Port": Der interne Port der Anwendung im Container ist nicht korrekt angegeben.
- Die Anwendung und NPM sind nicht im selben Docker-Netzwerk.
- Die Anwendung selbst läuft nicht korrekt im Container.
Lösung:
- Überprüfe im NPM unter "Proxy Hosts", ob der "Forward Hostname / IP" exakt dem
container_namedeiner Anwendung entspricht (in unserem Beispielmy-web-app) und der "Forward Port" korrekt ist (in unserem Beispiel80). - Stelle sicher, dass beide
docker-compose.ymlDateien (NPM und deine App) dasproxy-networkkorrekt referenzieren undexternal: truegesetzt ist. Du kannst das überprüfen mitdocker inspect.| grep -A 5 Networks - Schau in die Logs deiner Anwendung, um zu sehen, ob sie überhaupt gestartet ist und Fehler wirft:
docker logs my-web-app. - Versuche, vom NPM-Container aus deine App zu pingen:
docker exec -it nginx-proxy-manager ping my-web-app. Wenn das nicht geht, ist etwas mit der Namensauflösung im Docker-Netzwerk faul.
3. Let's Encrypt Zertifikat schlägt fehl
Fehlermeldung: Im NPM siehst du eine Fehlermeldung im SSL-Tab, dass das Zertifikat nicht ausgestellt werden konnte, oft mit Hinweisen auf HTTP-01 Challenges oder DNS-Probleme.
Problem: Let's Encrypt konnte deine Domain nicht erreichen, um zu verifizieren, dass du der Besitzer bist. Häufige Ursachen:
- Port-Weiterleitung fehlt oder ist falsch: Die Ports 80 und 443 sind nicht von deinem Router auf deinen Server weitergeleitet.
- Firewall auf dem Server: Eine Firewall (z.B. UFW) auf deinem Linux-Server blockiert die Ports 80 oder 443.
- Falsche DNS-Einträge: Deine Domain zeigt nicht auf die externe IP-Adresse deines Routers.
- Domain nicht von außen erreichbar: Dein Router hat vielleicht kein "Loopback" (Hairpin NAT), sodass du die Domain von deinem internen Netzwerk aus nicht erreichen kannst, Let's Encrypt aber schon.
Lösung:
- Router-Konfiguration: Prüfe deine Port-Weiterleitungen im Router. Port 80 (TCP) und 443 (TCP) müssen auf die interne IP deines Docker-Servers zeigen.
- Server-Firewall: Wenn du UFW nutzt, stelle sicher, dass die Ports offen sind:
sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw reload - DNS-Check: Nutze Tools wie
dig deine-domain.deoder Online-DNS-Checker, um zu prüfen, ob deine Domain auf die korrekte externe IP-Adresse zeigt. - Let's Encrypt Log: Schau in die Logs des NPM-Containers, um detailliertere Fehlermeldungen von Let's Encrypt zu sehen:
docker logs nginx-proxy-manager. - DNS-Challenge: Wenn die HTTP-Challenge (über Port 80) immer wieder fehlschlägt, weil dein Server z.B. hinter einem CGNAT steckt oder die Ports nicht öffnen kann, kannst du eine DNS-Challenge versuchen. Dafür musst du im NPM unter SSL bei der Zertifikatsanfrage den Haken bei "Use DNS Challenge" setzen und die API-Schlüssel deines DNS-Providers hinterlegen. Das ist etwas aufwendiger, aber sehr zuverlässig.
Fazit und nächste Schritte
Herzlichen Glückwunsch! Du hast nun einen extrem leistungsfähigen und flexiblen Docker Compose Stack mit Nginx Proxy Manager am Laufen. Das ist, wie ich finde, eine der besten Grundlagen für jedes ernstzunehmende Homelab. Du hast gelernt, wie man Docker-Netzwerke sinnvoll einsetzt, Dienste isoliert und sie elegant über einen Reverse Proxy mit SSL von Let's Encrypt erreichbar macht. Kein Port-Chaos mehr, keine manuellen Zertifikats-Updates – einfach nur Freude am Homelab!
Was sind die nächsten Schritte? Die Möglichkeiten sind endlos:
- Weitere Dienste hinzufügen: Jetzt kannst du nach dem gleichen Schema Home Assistant, n8n, Vaultwarden, Grafana, Portainer oder was auch immer dein Herz begehrt, in einem eigenen
docker-compose.ymlaufsetzen und über NPM erreichbar machen. - Authentifizierung: NPM bietet auch grundlegende Authentifizierungsmöglichkeiten (z.B. Basic Auth) direkt im Proxy Host.
- Sicherheit: Denk über ein Tool wie Fail2Ban nach, das in Verbindung mit NPM (z.B. durch Parsen der NPM-Logs) Brute-Force-Angriffe abwehren kann.
- Monitoring: Integriere Prometheus und Grafana, um die Performance deiner Docker-Dienste und des Hosts zu überwachen.
Ich hoffe, dieser Guide hilft dir, dein Homelab auf das nächste Level zu heben. Teile deine Erfahrungen und Fragen gerne in den Kommentaren auf smoth.me – dafür sind wir ja eine Community!