Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
| Category | Core Services |
|---|---|
| Platform | nixos |
| Version | 2.11.4 |
| Site link | https://caddyserver.com |
| Nix Homelab Module | modules/features/caddy |
Caddy is a modern web server and reverse proxy with
automatic HTTPS. In this homelab, Caddy is used as the public HTTPS entrypoint
for services exposed with services.caddy.virtualHosts.
The module configures Caddy with the Hetzner DNS plugin so certificates can be issued through DNS-01 challenges.
Automatic HTTPS with DNS-01 certificates and simple reverse proxying
Key benefits:
- Automatic Certificates: Caddy obtains and renews HTTPS certificates automatically
- DNS-01 Support: Hetzner DNS challenges work without opening HTTP port
80 - Reverse Proxy: Public domains proxy to local services listening on
127.0.0.1 - Centralized Logs: Public access logs are written as JSON for GoAccess and observability
homelab.features = {
acme.enable = true;
acme.email = config.homelab.domainEmailAdmin;
acme.dnsProvider = "hetzner";
caddy.enable = true;
};The Caddy module builds Caddy with the Hetzner DNS provider:
package = pkgs.caddy.withPlugins {
plugins = [ "github.com/caddy-dns/hetzner/v2@v2.0.0" ];
hash = "sha256-pQJ4X7o8Z2Ra2OteMrzP7guWcxBe4zfn8jFwIAdQ+Ow=";
};The Hetzner API token is stored through Clan vars and exposed to Caddy as:
HETZNER_API_TOKEN=<token>The module uses the authoritative Hetzner nameservers for propagation checks:
cert_issuer acme {
dns hetzner {$HETZNER_API_TOKEN}
propagation_delay 60s
propagation_timeout 10m
resolvers 213.133.100.102 213.239.204.242 193.47.99.4
}
auto_https disable_redirectsauto_https disable_redirects disables only the automatic HTTP-to-HTTPS
redirect listener. HTTPS certificate management remains enabled.
Each public service declares its own Caddy virtual host. Example:
services.caddy.virtualHosts = mkIf cfg.openFirewall {
"${cfg.serviceDomain}" = {
logFormat = ''
output file /var/log/caddy/public.log {
mode 0644
}
format json
'';
extraConfig = ''
reverse_proxy 127.0.0.1:${toString listenHttpPort}
'';
};
};The application container or service should keep listening locally, usually on
127.0.0.1:<port>. Caddy handles the public TLS endpoint.
systemctl status caddy
journalctl -u caddy -b --no-pager -n 200caddy list-modules | grep hetznerExpected module:
dns.providers.hetzner
journalctl -u caddy -b | grep tls.obtainCommon DNS-01 error:
NXDOMAIN looking up TXT for _acme-challenge.<domain>
This means Let's Encrypt did not see the DNS challenge TXT record. Check that:
- the Hetzner token is valid
- the token can write to the correct DNS zone
- the domain uses the expected Hetzner authoritative nameservers
- propagation delay is long enough for secondary validation
curl -v https://example.ma-cabane.eu/
openssl s_client -connect example.ma-cabane.eu:443 -servername example.ma-cabane.euIf the TLS handshake returns tlsv1 alert internal error, Caddy is usually
reachable on :443 but cannot present a valid certificate yet. Check
journalctl -u caddy for the ACME error.