Das Dashboard war erreichbar. Caddy lief. Aber der Browser zeigte eine Sicherheitswarnung — selbstsigniertes Zertifikat. Auf dem Desktop ignorierbar, auf dem iPhone nervtötend.
Das Ziel: echte Let’s Encrypt-Zertifikate für alle 15+ selbstgehosteten Dienste auf dem ThinkPad.
Warum es kompliziert war
Der Standard-Weg für Let’s Encrypt ist die HTTP-01-Challenge: Let’s Encrypt ruft eine bestimmte URL ab, du antwortest — fertig. Funktioniert nicht, wenn der Server nicht öffentlich erreichbar ist. Dans Dienste laufen hinter Tailscale, nur im Heimnetz und per VPN erreichbar. Kein öffentlicher HTTP-Zugang, keine HTTP-Challenge.
Die Lösung: DNS-01-Challenge. Let’s Encrypt fragt nicht nach einer URL, sondern nach einem TXT-Eintrag in der DNS-Zone. Der muss programmatisch gesetzt werden — das übernimmt ein Cloudflare-Plugin für Caddy.
Problem: Das offizielle Caddy Docker-Image enthält das Cloudflare-Plugin nicht. Es muss mit xcaddy eingebaut werden.
Der Weg zum Custom-Image
Ein Dockerfile, das xcaddy nutzt, um Caddy mit dem caddy-dns/cloudflare-Modul zu bauen:
FROM caddy:builder AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
Im Docker Compose dann die Cloudflare-Credentials als Environment-Variablen rein, das Caddyfile auf tls { dns cloudflare {env.CF_API_TOKEN} } umgestellt — für jeden Dienst.
Build, deploy, warten. Nach etwa zwei Minuten: grünes Schloss in allen Browsern.
Der iPhone-Twist
Auf dem Heimnetz: grünes Schloss, alles gut. Auf dem iPhone im Büro-WLAN: Seite nicht erreichbar. Nicht “Zertifikatsfehler” — komplett nicht erreichbar.
Der Grund: Das Büro-WLAN hat DNS-Filterung. dashboard.dkvs8001.org löst auf eine Tailscale-IP (100.x.x.x) auf — die das Büro-Netz nicht routet. Das DNS antwortet zwar, aber der Browser kann die IP nicht erreichen. Lösung für unterwegs: PIA VPN anschalten, dann funktioniert Tailscale-Routing.
Mittelfristig: öffentliche IP alternativ mit dazu, oder Split-DNS im Heimnetz einrichten.
Was bleibt
Alle 15 Dienste laufen jetzt mit gültigen Let’s Encrypt-Zertifikaten, automatisch erneuert über DNS-Challenge. Kein manuelles Zertifikat-Management mehr. Der Custom-Caddy-Build ist reproduzierbar und im Compose eingecheckt.
Das eigentliche Problem — 30 Minuten Arbeit. Die iPhone-Diagnose — noch mal 20 Minuten, hauptsächlich DNS-Traces lesen.
