Caddy als Docker-Reverse-Proxy: Compose-Setup mit automatischem HTTPS
Caddy als Docker-Reverse-Proxy betreiben: ein Compose-Stack, ein gemeinsames Netzwerk, reverse_proxy auf Service-Namen und automatisches HTTPS — plus wann caddy-docker-proxy-Labels helfen.
Kurz gesagt: Betreiben Sie Caddy in einem eigenen Container, der die Ports 80 und 443 veröffentlicht, hängen Sie ihn und Ihre App-Container an ein gemeinsames Docker-Netzwerk und richten Sie `reverse_proxy` auf den jeweiligen Service-Namen und internen Port (zum Beispiel `reverse_proxy app:3000`). Caddy löst den Namen über Dockers eingebautes DNS auf und holt automatisch ein TLS-Zertifikat für jede Site-Adresse.
Diese Anleitung zeigt zuerst den Ansatz mit einem einfachen Caddyfile, dann die label-gesteuerte Variante caddy-docker-proxy, und erklärt, wann welche auf einem einzelnen VPS passt. Wenn Sie Caddy direkt auf dem Host statt in Docker betreiben, siehe die grundlegende Anleitung zu Caddy als Reverse Proxy.
Wie die Teile zusammenspielen
Nur der Caddy-Container veröffentlicht Ports nach außen. Jeder Backend-Container tritt einem benutzerdefinierten Bridge-Netzwerk bei (hier `web`) und stellt seinen Port nur in diesem Netzwerk bereit. Der eingebaute DNS-Server von Docker, verfügbar in benutzerdefinierten Netzwerken, lässt Caddy `app:3000` per Service- oder Container-Name erreichen — Sie müssen also nie Container-IPs fest eintragen, die sich bei jedem Neuerstellen ändern.
Backends nicht zu veröffentlichen ist wichtiger, als es aussieht: Docker schreibt eigene iptables-Regeln, sodass ein mit `ports:` veröffentlichter Port aus dem Internet erreichbar sein kann, selbst wenn ufw ihn als blockiert anzeigt. Nutzen Sie `expose:` oder gar kein Port-Mapping für alles, was hinter dem Proxy liegen soll.
| Komponente | Rolle | Auf dem Host veröffentlicht? |
|---|---|---|
| caddy-Container | TLS-Terminierung, Routing nach Hostname, Umleitung von HTTP auf HTTPS | Ja: 80/tcp, 443/tcp, 443/udp |
| web-Netzwerk | Gemeinsames Bridge-Netzwerk mit eingebautem DNS für Service-Namen | Nein |
| App-Container | Ihre Dienste (Web-Apps, APIs, Dashboards) | Nein, nur über Caddy erreichbar |
| caddy_data-Volume | Zertifikate, private Schlüssel, ACME-Konto | Nein, muss aber persistent sein |
Schritt für Schritt: einfaches Caddyfile mit docker compose
- 01.Lassen Sie vor dem Start von Caddy für jeden Hostnamen einen A-Record (und einen AAAA-Record, wenn Sie IPv6 nutzen) auf den VPS zeigen, sonst schlägt die Zertifikatsausstellung fehl.
- 02.Legen Sie das gemeinsame Netzwerk einmalig an: `docker network create web`.
- 03.Erstellen Sie `/opt/proxy/compose.yaml` und `/opt/proxy/Caddyfile` wie unten gezeigt und führen Sie dann `docker compose up -d` in `/opt/proxy` aus.
- 04.Hängen Sie jeden App-Stack an das externe Netzwerk `web` und starten Sie ihn mit `docker compose up -d`.
- 05.Verfolgen Sie die Ausstellung des ersten Zertifikats mit `docker compose logs -f caddy` in `/opt/proxy`.
- 06.Nach Änderungen am Caddyfile laden Sie ohne Downtime neu: `docker compose exec -w /etc/caddy caddy caddy reload`.
# /opt/proxy/compose.yaml
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data # certificates and ACME account keys
- caddy_config:/config
networks:
- web
volumes:
caddy_data:
caddy_config:
networks:
web:
external: trueEine App anbinden und das Caddyfile schreiben
Jeder Anwendungs-Stack deklariert dasselbe externe Netzwerk. Der Service-Name wird zum Hostnamen, den Caddy anwählt. Das Volume `caddy_data` dürfen Sie nie verlieren: Wird es gelöscht, muss Caddy jedes Zertifikat neu anfordern, was in die ACME-Rate-Limits laufen kann.
# /opt/app/compose.yaml
services:
app:
image: ghcr.io/example/app:latest
restart: unless-stopped
expose:
- "3000" # reachable on the network, NOT published on the host
networks:
- web
networks:
web:
external: true
# /opt/proxy/Caddyfile
{
email [email protected]
}
app.example.com {
reverse_proxy app:3000
}
grafana.example.com {
reverse_proxy grafana:3000
}
api.example.com {
reverse_proxy api-1:8080 api-2:8080 {
lb_policy round_robin
health_uri /healthz
health_interval 10s
}
}Caddy-Backends: Upstreams, Load Balancing und Health Checks
In Caddy-Begriffen ist ein Backend ein Upstream von `reverse_proxy`. Sie können mehrere Upstreams in einer Direktive angeben, eine `lb_policy` wie `round_robin`, `least_conn` oder `ip_hash` wählen und mit `health_uri` aktive Health Checks hinzufügen, damit ausgefallene Container aus der Rotation fallen. Passive Checks (`fail_duration`, `max_fails`) sind ebenfalls nützlich, wenn die App keinen Health-Endpunkt hat.
Caddy setzt bei weitergeleiteten Anfragen standardmäßig `X-Forwarded-For`, `X-Forwarded-Proto` und `X-Forwarded-Host`. Konfigurieren Sie Ihre App so, dass sie dem Netzbereich des Proxys vertraut, damit sie die echte Client-IP sieht und `https://`-Links erzeugt. WebSockets und gRPC funktionieren über `reverse_proxy` ohne zusätzliche Einstellungen; für gRPC im Klartext nutzen Sie `reverse_proxy h2c://service:50051`.
Labels statt Caddyfile: caddy-docker-proxy
Wer nach einem „Caddy Proxy Manager“ sucht, will meist das, was Nginx Proxy Manager bietet: Container hinzufügen, Hostname und Zertifikat bekommen, ohne eine zentrale Datei zu bearbeiten. Caddy hat keine offizielle GUI, aber das Community-Projekt caddy-docker-proxy (github.com/lucaslorentz/caddy-docker-proxy) erzeugt das Caddyfile aus Container-Labels und lädt Caddy neu, wenn Container starten oder stoppen.
Es braucht Lesezugriff auf den Docker-Socket, was faktisch Root auf dem Host bedeutet. Binden Sie ihn schreibgeschützt ein, halten Sie das Proxy-Image aktuell und bevorzugen Sie das einfache Caddyfile, wenn Sie nur eine Handvoll Dienste betreiben.
services:
caddy:
image: lucaslorentz/caddy-docker-proxy:ci-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- CADDY_INGRESS_NETWORKS=web
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- caddy_data:/data
networks:
- web
whoami:
image: traefik/whoami
networks:
- web
labels:
caddy: whoami.example.com
caddy.reverse_proxy: "{{upstreams 80}}"
volumes:
caddy_data:
networks:
web:
external: trueEinfaches Caddyfile vs. caddy-docker-proxy
| Einfaches Caddyfile | caddy-docker-proxy-Labels | |
|---|---|---|
| Konfiguration liegt in | Einer Datei, die Sie in git versionieren | Labels, verteilt über Compose-Dateien |
| Zugriff auf den Docker-Socket | Nicht nötig | Erforderlich (sicherheitskritisch) |
| Neuer Dienst | Caddyfile bearbeiten, `caddy reload` ausführen | Labels hinzufügen, Container starten |
| Image | Offizielles `caddy:2` | Community-Image auf Caddy-Basis |
| Debugging | Datei direkt lesen | Generierte Konfiguration in den Logs prüfen |
| Am besten für | Kleine, stabile VPS-Setups | Viele kurzlebige oder häufig geänderte Dienste |
Betrieb auf einem VPS
Auf einem NoctisNode-KVM-VPS haben Sie vollen Root-Zugriff und eine dedizierte IPv4, sodass es unkompliziert ist, 80 und 443 aus einem Caddy-Container zu veröffentlichen und alles andere zu verbergen. Ein Wisp (1 vCPU, 1 GB RAM) reicht für Caddy plus ein paar leichte Container; schwerere Stacks passen auf Pulse oder Umbra. Details und Fehlerbilder der Zertifikatsausstellung behandelt So funktioniert Caddy Automatic HTTPS, reines TCP- oder SSH-Forwarding steht in TCP- und SSH-Proxying mit Caddy. Sichern Sie SSH auf dem Host mit der Anleitung zur SSH-Härtung ab.
FAQ
QWie richte ich Caddy als Reverse Proxy für einen Docker-Container ein?
ABringen Sie Caddy und den Container in dasselbe benutzerdefinierte Docker-Netzwerk und verwenden Sie `reverse_proxy service-name:port` im Caddyfile. Nutzen Sie nicht `localhost`, denn im Caddy-Container bezeichnet das Caddy selbst.
QWarum liefert Caddy 502 Bad Gateway für meinen Container?
AMeist sind die Container nicht in einem gemeinsamen Netzwerk, der Port ist falsch (nutzen Sie den internen Port des Containers, nicht einen veröffentlichten Host-Port) oder die App lauscht in ihrem Container nur auf 127.0.0.1. Lassen Sie die App an 0.0.0.0 binden und prüfen Sie `docker network inspect web`.
QIst caddy-docker-proxy sicher?
AEs funktioniert gut, benötigt aber den Docker-Socket, der root-gleiche Kontrolle über den Host gewährt. Binden Sie ihn schreibgeschützt ein, halten Sie das Image aktuell und machen Sie den Admin-Endpunkt von Caddy nicht öffentlich zugänglich.
QWo speichert Caddy in Docker die Zertifikate?
AIm offiziellen Image unter `/data`, also binden Sie dort ein benanntes Volume ein. Geht es verloren, müssen alle Zertifikate neu ausgestellt werden, womöglich bis an die ACME-Rate-Limits.
QGibt es einen Caddy Proxy Manager mit Web-Oberfläche?
ACaddy selbst hat keine offizielle Web-Oberfläche; es wird per Caddyfile, JSON oder über seine Admin-API konfiguriert. Label-basierte Tools wie caddy-docker-proxy bieten einen ähnlichen Ablauf nach dem Motto „Container hinzufügen und los“.