Erstelle mir eine detaillierte Anleitung, wie ich Portainer und Nginx Proxy Manager als Reverse Proxy in einer Docker-Umgebung auf einem VPS remote installieren muss. Ich möchte remote auf die Weboberflächen zugreifen können, jedoch soll der Zugang stark abgesichert sein.
meinedomain.de)apt update && apt upgrade -y
adduser deploy
usermod -aG sudo deploy
Lokal einen SSH-Key generieren (falls noch keiner vorhanden):
# Auf deinem lokalen Rechner
ssh-keygen -t ed25519 -C "vps-deploy"
Public Key auf den Server kopieren:
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@DEINE_VPS_IP
SSH-Konfiguration härten – /etc/ssh/sshd_config:
# Folgende Werte setzen / anpassen:
Port 2222 # Nicht-Standard-Port
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
MaxAuthTries 3
LoginGraceTime 30
SSH neu starten:
systemctl restart sshd
Achtung: Teste die neue SSH-Verbindung in einem zweiten Terminal, bevor du die alte schließt!
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp # Dein SSH-Port
ufw allow 80/tcp # HTTP (für Let's Encrypt)
ufw allow 443/tcp # HTTPS
ufw enable
ufw status verbose
Portainer (9000/9443) und NPM (81) werden nicht direkt freigegeben – der Zugang erfolgt ausschließlich über den Reverse Proxy!
apt install fail2ban -y
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = 2222
EOF
systemctl enable --now fail2ban
# Abhängigkeiten
apt install -y ca-certificates curl gnupg
# Docker GPG-Key
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
# Repository hinzufügen
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Deploy-User zur Docker-Gruppe hinzufügen
usermod -aG docker deploy
Verifizieren:
docker --version
docker compose version
mkdir -p /opt/docker/{npm,portainer}
cd /opt/docker
Datei /opt/docker/docker-compose.yml:
services:
# ─── Nginx Proxy Manager ───────────────────────────────────────
npm:
image: jc21/nginx-proxy-manager:latest
container_name: npm
restart: unless-stopped
ports:
# - "80:80" # wg. NPM gesperrt; HTTP → Let's Encrypt Challenge
- "443:443" # HTTPS
# Port 81 (Admin-UI) wird NICHT nach außen exponiert!
- "127.0.0.1:81:81" # öffnet nur auf dem host, nicht für's Internet
volumes:
- ./npm/data:/data
- ./npm/letsencrypt:/etc/letsencrypt
networks:
- proxy
# ─── Portainer ─────────────────────────────────────────────────
portainer:
image: portainer/portainer-ce:latest
container_name: portainer
restart: unless-stopped
# Keine ports: – kein direkter Außenzugang!
ports:
- "9090:9000" # doch für Internet geöffnet, nur https
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./portainer:/data
networks:
- proxy
networks:
proxy:
name: proxy
driver: bridge
Stack starten:
cd /opt/docker
docker compose up -d
docker compose ps
Beim DNS-Anbieter deiner Domain folgende A-Records anlegen:
| Name | Typ | Wert |
|---|---|---|
npm.meinedomain.de |
A | DEINE_VPS_IP |
portainer.meinedomain.de |
A | DEINE_VPS_IP |
TTL: 300 (für die Ersteinrichtung).
Das Einrichten beim DNS-Anbieter (Cloudflare oa) und Erzeugen der SSL-Zertifikate kann etwas tricky sein. Dafür gibt es eine eigene Anleitung!
Da Port 81 nicht nach außen offen ist, temporärer SSH-Tunnel:
# Lokal ausführen:
ssh -L 8181:localhost:81 -p 2222 deploy@DEINE_VPS_IP -N
Browser öffnen: http://localhost:8181
Standard-Login:
admin@example.comchangemeSofort E-Mail und Passwort auf sichere Werte ändern!
NPM → Proxy Hosts → Add Proxy Host
Details Tab:
Domain Names: portainer.meinedomain.de
Scheme: http
Forward Hostname: portainer ← Docker-Containername
Forward Port: 9000
SSL Tab:
SSL Certificate: Let's Encrypt (neu beantragen)
Force SSL: ✓
HSTS Enabled: ✓
HTTP/2 Support: ✓
Domain Names: npm.meinedomain.de
Scheme: http
Forward Hostname: npm
Forward Port: 81
SSL: Let's Encrypt, Force SSL, HSTS
Wichtig: Ohne zusätzliche Absicherung (siehe Phase 8) solltest du den NPM-Admin nicht öffentlich exponieren. Nutze lieber dauerhaft den SSH-Tunnel.
Für Portainer (und ggf. NPM-Admin) in NPM unter dem jeweiligen Proxy Host:
Advanced Tab → Custom Nginx Configuration:
# Basic Auth
auth_basic "Restricted";
auth_basic_user_file /data/nginx/basicauth/portainer;
# Rate Limiting
limit_req zone=one burst=10 nodelay;
Passwort-Datei erstellen (im NPM-Container):
docker exec -it npm sh
apk add --no-cache apache2-utils
mkdir -p /data/nginx/basicauth
htpasswd -c /data/nginx/basicauth/portainer DEIN_USERNAME
exit
Im NPM Advanced Tab:
# Nur deine IP erlauben
allow 1.2.3.4; # Deine IP
deny all;
Wireguard auf dem VPS installieren – Admin-UIs nur über VPN erreichbar:
apt install wireguard -y
Konfiguration /etc/wireguard/wg0.conf (Beispiel):
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
Firewall:
ufw allow 51820/udp # Wireguard
NPM/Portainer dann nur auf 10.0.0.1 binden (in docker-compose.yml):
ports:
- "10.0.0.1:80:80"
- "10.0.0.1:443:443"
Nach dem ersten Login über https://portainer.meinedomain.de:
# Zur docker-compose.yml hinzufügen:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true # alte Images nach Update löschen
- WATCHTOWER_SCHEDULE=0 0 4 * * * # täglich um 04:00 Uhr (cron)
- TZ=Europe/Berlin
networks:
- proxy
# NPM-Logs
docker logs npm -f
# Portainer-Logs
docker logs portainer -f
# Fail2Ban-Status
fail2ban-client status sshd
# Crontab für tägliches Backup
crontab -e
# Eintrag:
0 3 * * * tar -czf /backup/docker-$(date +\%Y\%m\%d).tar.gz /opt/docker && \
find /backup -name "docker-*.tar.gz" -mtime +7 -delete
| Maßnahme | Status |
|---|---|
| SSH Key-Only-Auth | ✓ |
| Nicht-Standard SSH-Port | ✓ |
| UFW Firewall | ✓ |
| Fail2Ban | ✓ |
| Admin-Ports nicht direkt exponiert | ✓ |
| HTTPS + HSTS für alle Dienste | ✓ |
| HTTP Basic Auth / IP-Whitelist | ✓ |
| VPN-Zugang (optional, empfohlen) | optional |
| Automatische SSL-Zertifikate | ✓ |
Empfehlung: Für maximale Sicherheit Variante C (VPN) wählen und Admin-UIs komplett hinter Wireguard verstecken. Dann brauchen NPM und Portainer gar nicht öffentlich erreichbar zu sein.