← Dev log

networking

Caddy, because a lapsed cert is a solo problem

Caddy, because a lapsed cert is a solo problem

Several services need to be reachable through one server on 80/443 — the API, the web app, the admin panel, the operations console — but each runs as its own container on its own internal port. A reverse proxy terminates the incoming connection and routes each request to the right service by domain or path.

nginx is the long-standing default, extremely capable, and its config is verbose with HTTPS as a separate manual concern — traditionally Certbot plus a hand-wired renewal cron. Caddy's defining feature is automatic HTTPS: point it at a domain and it gets and renews a Let's Encrypt certificate on its own.

api.example.com {
  reverse_proxy backend:2000
}
admin.example.com {
  reverse_proxy admin:5002
}

That's close to the whole config for HTTPS-terminated routing to two services — genuinely shorter than the nginx-plus-Certbot equivalent.

Why it matters for one person

Every piece of infrastructure that needs periodic manual attention — renewing certs, remembering a cron job, debugging a lapsed one — is an ongoing cost, not a one-time setup. Automatic renewal removes a whole category of "something quietly expires and breaks production because nobody was watching," which matters most exactly when there's no team to catch it.

Internet → Cloudflare (CDN/DDoS/DNS) → Caddy (reverse proxy, TLS, routing) → Docker containers

Caddy knows which container handles which domain. Cloudflare sits further out — DNS, CDN caching, DDoS protection — before traffic reaches the server at all.

Caddy handles routing and TLS termination. It isn't rate limiting, isn't a WAF, isn't DDoS protection on its own — those live at the Cloudflare layer, or need adding explicitly. It's also where I moved a maintenance-mode check that had been living in client-side JS: a control that depends on the controlled client successfully making a network call to learn it should block itself is not a control.