# Quadlet unit: canonical Caddy reverse proxy + ACME (rootless, boot-persistent). # # Install path on dankclaw: ~/.config/containers/systemd/caddy.container # Unit name: caddy.service (container name: caddy) # # The homelab's single Caddy: it fronts every service on both hosts and issues # Let's Encrypt wildcard certs via DNS-01 (Porkbun). It left compose.yaml on # 2026-08-26 after a reboot exposed that the bare compose container never came # back: rootless Podman has no boot-time auto-start for bare containers, and # `restart: unless-stopped` does not survive the explicit stop that shutdown # performs. See AGENTS.md "Boot Persistence & Self-Healing" and # serverAppConfigs-docs/Infrastructure/Caddy.md. # # It joins the existing compose network (dankclaw_default) so the Caddyfile's # container-name upstreams (authentik-server, grafana, ...) still resolve; if # no compose service has created that network yet at boot, quadlet creates it # first and compose reuses it by name later. The image is built locally from # dankclaw/caddy/Containerfile and exists only on this host, so Pull=never: a # boot must not stall on a registry that does not have it. Porkbun credentials # come from /home/dank/Containers/caddy/env (deploy writes it from the # checkout's .env; secrets never enter the repo). [Unit] Description=Caddy reverse proxy + ACME (Porkbun DNS-01) container After=network-online.target Wants=network-online.target [Container] ContainerName=caddy Image=franksdank/caddy-porkbun:latest Pull=never Network=dankclaw_default PublishPort=0.0.0.0:80:80/tcp PublishPort=0.0.0.0:443:443/tcp EnvironmentFile=/home/dank/Containers/caddy/env Volume=/home/dank/Containers/caddy/Caddyfile:/etc/caddy/Caddyfile:ro Volume=/home/dank/Containers/caddy/data:/data Volume=/home/dank/Containers/caddy/config:/config # The homepage: the host's /srv/homepage (deploy-managed, not the repo's # shared/homepage). Volume=/srv/homepage:/srv/homepage:ro # The bare-IP catch-all answers 302 (curl -f passes on <400); this proves # Caddy itself is serving without touching any upstream. HealthCmd=curl -sf -o /dev/null http://localhost:80/ HealthInterval=30s HealthTimeout=10s HealthRetries=3 [Service] Restart=always RestartSec=5 [Install] WantedBy=default.target