Caddy als Reverse Proxy einrichten: automatisches HTTPS, mehrere Domains und Weboberfläche
Caddy 2.11 mit Docker Compose als Reverse Proxy einrichten: Zertifikate automatisch per Let's Encrypt, mehrere Domains im Caddyfile, Reload ohne Ausfall, Backup mit Wiederherstellung und optional Domains per CaddyUI-Weboberfläche verwalten.
Geprüft am 11.10.2026 · für Caddy 2.11.7, CaddyUI 2.63.2
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Caddy ist ein Webserver, der HTTPS-Zertifikate selbst beantragt, speichert und erneuert. Als Reverse Proxy nimmt er Anfragen auf den Ports 80 und 443 entgegen und reicht sie anhand des Hostnamens an interne Dienste weiter. Certbot, Cron-Jobs und Zertifikatspfade entfallen. Diese Anleitung richtet Caddy 2.11 mit Docker Compose ein, bindet zwei Beispieldienste unter eigenen Domains an und zeigt zum Schluss die Community-Oberfläche CaddyUI, mit der Sie Domains per Formular statt per Konfigurationsdatei eintragen.
Voraussetzungen
- Linux-Server (VPS oder Homeserver) mit x86_64 oder ARM64 und öffentlicher IPv4-Adresse. Für Caddy genügen 1 CPU-Kern und 512 MB RAM. Im Test belegte Caddy rund 20 MB RAM, CaddyUI weitere rund 28 MB. Den eigentlichen Bedarf bestimmen die Dienste hinter dem Proxy.
- Docker Engine mit Docker Compose v2.
- Eine eigene Domain mit Zugriff auf die DNS-Verwaltung.
- Ports 80 und 443 (TCP, für HTTP/3 zusätzlich 443 UDP) von außen erreichbar und auf dem Server frei. Port 80 braucht Let's Encrypt für die HTTP-01-Challenge.
- Mindestens ein Dienst, den Caddy erreichbar machen soll, idealerweise als Container im selben Compose-Projekt.
Schritt 1: DNS-Eintrag setzen
Let's Encrypt prüft vor der Ausstellung, ob die Domain auf Ihren Server zeigt. Legen Sie deshalb in der DNS-Verwaltung für jede Subdomain einen A-Record auf die öffentliche IPv4 des Servers an, zum Beispiel app.example.de und dashboard.example.de auf 203.0.113.10. Hat der Server IPv6, ergänzen Sie einen AAAA-Record. Ein AAAA-Record, der nicht auf diesen Server zeigt, lässt die Challenge ebenfalls scheitern.
dig +short app.example.de
dig +short AAAA app.example.deVerifizieren: Die erste Abfrage liefert genau die IPv4 des Servers, die zweite nichts oder die richtige IPv6. Erst dann geht es weiter.
Schritt 2: Projektordner und Caddyfile anlegen
Das Caddyfile ist die Konfigurationsdatei von Caddy. Binden Sie den Ordner conf ein, nicht die Datei allein: Viele Editoren speichern eine neue Datei statt die alte zu überschreiben, und ein Reload sieht die Änderung dann nicht. Darauf weist auch die Dokumentation des offiziellen Images hin.
mkdir -p ~/caddy/conf && cd ~/caddy
nano conf/CaddyfileJeder Block beginnt mit dem Hostnamen und nennt das Ziel. web und whoami sind die Dienstnamen aus der Compose-Datei in Schritt 3, Docker löst sie im gemeinsamen Netz auf.
app.example.de {
reverse_proxy web:80
}
dashboard.example.de {
reverse_proxy whoami:80
}Für jeden Block aktiviert Caddy HTTPS samt Umleitung von HTTP. Läuft ein Dienst direkt auf dem Host statt im Compose-Netz, tragen Sie reverse_proxy host.docker.internal:8080 ein und ergänzen beim Caddy-Dienst extra_hosts: ["host.docker.internal:host-gateway"]. localhost zeigt im Container auf den Caddy-Container selbst.
Verifizieren: cat conf/Caddyfile zeigt je Domain einen Block mit reverse_proxy. Die Syntaxprüfung folgt in Schritt 5.
Schritt 3: Compose-Datei erstellen
Legen Sie compose.yaml im Ordner ~/caddy an. Die Dienste web und whoami sind Beispiel-Backends, die Sie später durch Ihre Anwendungen ersetzen. Sie veröffentlichen keine Ports, erreichbar sind sie nur über Caddy.
services:
caddy:
image: caddy:2.11.7
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy
- caddy_data:/data
- caddy_config:/config
web:
image: nginx:1.29-alpine
restart: unless-stopped
whoami:
image: traefik/whoami:v1.11
restart: unless-stopped
volumes:
caddy_data:
caddy_config:| Eintrag | Wert | Zweck |
|---|---|---|
| Image | caddy:2.11.7 | festgeschrieben, caddy:2.11 folgt Patch-Versionen |
| Ports | 80, 443, 443/udp | HTTP, HTTPS, HTTP/3 |
./conf | /etc/caddy | Caddyfile |
caddy_data | /data | Zertifikate, Schlüssel, ACME-Konto |
caddy_config | /config | automatisch gespeicherte Konfiguration |
Geht caddy_data verloren, beantragt Caddy alle Zertifikate neu. Bei häufigen Neustarts ohne dieses Volume greifen die Rate-Limits von Let's Encrypt.
Verifizieren: docker compose config gibt die Konfiguration ohne Fehler aus und listet die Volumes caddy_data und caddy_config.
Schritt 4: Caddy starten und Zertifikate prüfen
docker compose up -d
docker compose logs caddy | grep "certificate obtained"Beim ersten Start löst Caddy die ACME-Challenge und speichert das Zertifikat. Im Log steht je Domain eine Zeile mit certificate obtained successfully. Im Test ohne öffentliche Domain stellte Caddy 2.11.7 die Zertifikate mit der globalen Option local_certs aus der eigenen CA aus, die Zeilen erschienen wenige Sekunden nach dem Start, dort mit "issuer":"local". Mit echter Domain steht an dieser Stelle der ACME-Aussteller.
curl -I http://app.example.de
curl -I https://app.example.de
echo | openssl s_client -connect app.example.de:443 -servername app.example.de 2>/dev/null | openssl x509 -noout -issuer -datesIm Test antwortete HTTP mit 308 Permanent Redirect und Location: https://app.example.de/, HTTPS mit HTTP/2 200. Der zweite Dienst erhielt von Caddy die Header Via: 2.0 Caddy und X-Forwarded-For mit der Client-Adresse.
Verifizieren: docker compose ps zeigt alle drei Dienste als Up, beide Domains liefern über HTTPS Status 200, und openssl nennt als Issuer Let's Encrypt (oder ZeroSSL) mit einem Ablaufdatum in der Zukunft.
Schritt 5: Konfiguration ändern und neu laden
Nach jeder Änderung am Caddyfile prüfen Sie die Syntax und laden die Konfiguration ohne Neustart. Bestehende Verbindungen bleiben dabei erhalten.
docker compose exec -w /etc/caddy caddy caddy validate
docker compose exec -w /etc/caddy caddy caddy reloadEin Tippfehler fällt bei validate auf, bevor er den Betrieb stört. Im Test meldete Caddy für reverse_prxy: Caddyfile:7: unrecognized directive: reverse_prxy. Ein neu angehängter Block mit respond "OK" 200 war nach dem Reload sofort samt Zertifikat erreichbar. caddy fmt --overwrite formatiert die Datei, das klappt nur ohne :ro am Mount.
Verifizieren: validate endet mit Valid configuration, reload gibt keinen Fehler aus, und die geänderte Domain antwortet wie erwartet.
Schritt 6: Domains per Weboberfläche verwalten (optional)
Caddy selbst hat keine Weboberfläche. Wer Domains lieber per Formular einträgt, kann CaddyUI nutzen, ein Community-Projekt unter Apache-2.0-Lizenz, das nicht vom Caddy-Team stammt. Es schreibt die Konfiguration über die JSON-Admin-API in Caddy. Ein Caddyfile wird dabei nicht verwendet, entscheiden Sie sich deshalb für einen der beiden Wege. Die Oberfläche ist englisch, das Projekt erscheint in sehr kurzen Abständen neu: Am 7. und 8. Oktober 2026 kamen mindestens sechs Versionen. Schreiben Sie die Tags deshalb fest.
Die Compose-Datei des Projekts setzt CADDY_ADMIN: 0.0.0.0:2019. Im Test konnte damit jeder Container im selben Docker-Netz die Admin-API ohne Anmeldung abfragen (HTTP 200 auf /config/) und damit Caddy beliebig umkonfigurieren. Die folgende Variante bindet die API an localhost und hängt CaddyUI per network_mode in den Netz-Namensraum von Caddy. Damit lief die Oberfläche normal, der Zugriff aus einem Nachbarcontainer schlug fehl. Die Weboberfläche ist nur auf dem Server selbst erreichbar.
services:
caddy:
image: applegater/caddyui-caddy:v2.61.0
container_name: caddyui-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
- "127.0.0.1:8081:8080" # Weboberfläche nur lokal
volumes:
- caddy_data:/data
- caddy_config:/config
environment:
CADDY_ADMIN: localhost:2019 # Admin-API nicht ins Docker-Netz
TZ: Europe/Berlin
command:
- /bin/sh
- -c
- |
mkdir -p /config/caddy
[ -f /config/caddy/autosave.json ] || echo '{"admin":{"listen":"localhost:2019"}}' > /config/caddy/autosave.json
exec caddy run --config /config/caddy/autosave.json --resume --adapter json
caddyui:
image: applegater/caddyui:v2.63.2
container_name: caddyui
restart: unless-stopped
network_mode: "service:caddy"
depends_on:
- caddy
volumes:
- caddyui_data:/data
environment:
CADDYUI_DB: /data/caddyui.db
CADDYUI_LISTEN: :8080
CADDY_ADMIN_URL: http://localhost:2019
CADDYUI_SYNC_ON_START: "1"
TZ: Europe/Berlin
volumes:
caddy_data:
caddy_config:
caddyui_data:docker compose up -d
ssh -L 8081:127.0.0.1:8081 benutzer@server.example.de
# danach im Browser: http://localhost:8081Beim ersten Aufruf legen Sie das Administratorkonto an, ein Standardpasswort gibt es nicht. Aktivieren Sie anschließend unter Getting Started die Zwei-Faktor-Anmeldung. Über Publish service führt ein Assistent in vier Schritten zum Proxy Host: Hostname, Ziel, TLS-Richtlinie, Prüfung.




Unter Export Caddyfile erzeugt CaddyUI eine lesbare Fassung. Im Test enthielt sie je Host einen reverse_proxy-Block mit den Headern X-Forwarded-Host, X-Forwarded-Proto und X-Real-IP.
Verifizieren: Unter Getting Started steht bei Connect and verify Caddy der Status Online, der neue Host liefert über HTTPS Status 200, und docker compose logs caddyui meldet nach einem Neustart caddy synced server 1.
Schritt 7: Backup, Wiederherstellung und Updates
Sichern Sie den Ordner conf und das Volume caddy_data. Docker setzt den Projektnamen davor, bei ~/caddy heißt es caddy_caddy_data. Mit CaddyUI gehört zusätzlich caddyui_data dazu, dort liegt die Datenbank mit allen Hosts.
docker run --rm -v caddy_caddy_data:/data -v "$PWD":/backup alpine \
tar czf /backup/caddy_data_backup.tar.gz -C /data .Zur Wiederherstellung stoppen Sie den Stack, legen das Volume neu an und entpacken das Archiv hinein:
docker compose down
docker volume rm caddy_caddy_data
docker volume create caddy_caddy_data
docker run --rm -v caddy_caddy_data:/data -v "$PWD":/backup alpine \
tar xzf /backup/caddy_data_backup.tar.gz -C /data
docker compose up -dIm Test hatte das Zertifikat nach der Wiederherstellung dieselbe SHA-256-Prüfsumme wie vorher, und Caddy stellte kein neues aus. Updates spielen Sie ein, indem Sie den Tag in compose.yaml nach Lektüre der Release Notes anheben und docker compose pull sowie docker compose up -d ausführen. CaddyUI 2.62.0 ergänzt eine wählbare Sprache der Oberfläche (unter Profile, Standard für alle unter Settings); bisher sind vor allem Navigation und Menüs übersetzt. Im Test lief der Wechsel von CaddyUI 2.61.2 auf 2.62.0 per docker compose pull und docker compose up -d ohne Eingriff, die Anmeldeseite antwortete danach wie vorher, und die Admin-API blieb aus dem Docker-Netz heraus unerreichbar.
Für den Wechsel von CaddyUI 2.62.0 auf 2.63.2 sichern Sie zunächst die drei Volumes und behalten das Caddy-Image unverändert. Heben Sie nur den CaddyUI-Tag an und erstellen Sie die Oberfläche mit docker compose pull caddyui und docker compose up -d caddyui neu. Der Wechsel mit erhaltenen Volumes und die anschließende Neuinstallation von 2.63.2 lieferten im Praxistest HTTP 200 auf der Einrichtungsseite. Die Admin-API antwortete innerhalb des gemeinsamen Netz-Namensraums mit HTTP 200, aus einem Nachbarcontainer war sie nicht erreichbar. Bei einer neuen Installation enthält die anfängliche JSON-Konfiguration ausdrücklich admin.listen: localhost:2019; damit ist die lokale Bindung auch in der gespeicherten Konfiguration festgehalten.
Verifizieren: tar tzf caddy_data_backup.tar.gz | grep crt listet die Zertifikate, nach einer Wiederherstellung enthält das Log keine neue Zeile certificate obtained.
Typische Fehler
| Meldung | Ursache | Lösung |
|---|---|---|
| Zertifikat wird nicht ausgestellt | DNS zeigt woanders hin oder Port 80 ist gesperrt | dig prüfen, Port 80 in Firewall und Router öffnen |
502, im Log dial tcp: lookup web: i/o timeout | Backend gestoppt oder falscher Dienstname | docker compose ps, Name und Port im reverse_proxy prüfen |
Connection refused bei localhost:8080 | localhost ist der Caddy-Container | Dienstnamen oder host.docker.internal verwenden |
Bind for 0.0.0.0:443 failed: port is already allocated | anderer Webserver belegt den Port | Dienst stoppen oder auf anderen Port legen |
unrecognized directive | Tippfehler im Caddyfile | Zeilennummer aus der Meldung korrigieren, erneut validate |
Gegen die Rate-Limits von Let's Encrypt testen Sie neue Konfigurationen zuerst gegen die Staging-Umgebung: im globalen Block am Anfang des Caddyfiles acme_ca https://acme-staging-v02.api.letsencrypt.org/directory eintragen, danach wieder entfernen.
Häufige Fragen
Brauche ich Certbot oder einen Cron-Job?
Nein. Caddy beantragt und erneuert die Zertifikate selbst und speichert sie im Volume caddy_data.
Welche Ports muss ich öffnen?
80 und 443 TCP, für HTTP/3 zusätzlich 443 UDP. Die Backends brauchen keine offenen Ports.
Geht das auch nur im Heimnetz ohne öffentliche Domain?
Ja. Mit tls internal im Site-Block oder local_certs im globalen Block stellt Caddy Zertifikate aus der eigenen CA aus. Browser warnen, bis Sie das Root-Zertifikat aus caddy_data auf den Geräten als vertrauenswürdig hinterlegen.
Caddyfile oder CaddyUI?
Das Caddyfile ist versionierbar, offiziell dokumentiert und kommt ohne weiteren Dienst aus. CaddyUI lohnt sich, wenn mehrere Personen Domains pflegen oder Sie Zertifikatsstatus und Upstream-Prüfung in einer Oberfläche sehen möchten. Sie betreiben dann ein zusätzliches Programm mit Administratorrechten über Ihren Proxy.
Caddy oder Nginx?
Für überschaubare Setups ist Caddy schneller eingerichtet, weil HTTPS ohne Zusatzwerkzeug funktioniert. Nginx bietet mehr Stellschrauben bei sehr hoher Last, verlangt aber eine eigene Zertifikatslösung.
Fazit
DNS setzen, ein Caddyfile mit wenigen Zeilen schreiben, Docker Compose starten: Caddy übernimmt Zertifikate und Umleitung ohne weitere Werkzeuge. Sichern Sie caddy_data, prüfen Sie jede Änderung mit validate, und legen Sie die Admin-API nie offen ins Netz, auch nicht beim Einsatz einer Weboberfläche wie CaddyUI.
Weiterführende Anleitungen und Quellen
Fehlt noch die Container-Basis, hilft unsere Schritt-für-Schritt-Anleitung zur Docker-Installation. Einen Vergleich der Alternativen bietet Caddy als Reverse-Proxy im Vergleich mit nginx und Traefik, den klassischen Weg zeigt Nginx als Reverse Proxy mit TLS manuell einrichten. Weitere Tutorials finden Sie in der Kategorie Cloud.
Quellen: Caddy-Dokumentation: Reverse Proxy Quick-start, Caddy-Dokumentation: Automatic HTTPS, Caddy auf Docker Hub, Release Caddy 2.11.7, CaddyUI auf GitHub.


