Automatic HTTPS
Prepare public certificate issuance
Verify DNS, ports, permissions, and persistent storage before asking Caddy to obtain a public certificate.
Automatic HTTPS is one line of configuration and four external dependencies. When issuance fails, the Caddyfile is almost never the problem. The environment is.
Here is the checklist I go through before pointing a real domain at a server. Three checks, a few minutes, and most issuance failures never happen.
1. DNS
The name must resolve to this machine, in both address families:
dig +short A app.example.com
dig +short AAAA app.example.com
Compare both answers against the server’s real addresses.
The sneaky one is a stale AAAA record, the IPv6 address. IPv4 points at the new server while IPv6 still points at the old one. The CA may validate over IPv6, hit the wrong machine, and fail. You’re left staring at a config that looks perfect.
2. Ports
ACME challenges arrive on port 80 for the HTTP challenge and on port 443 for the TLS-ALPN challenge. Check that nothing else holds them:
sudo ss -lntp | grep -E ":80 |:443 "
The output should be empty, or show only Caddy. If nginx or Apache is sitting on those ports, Caddy either fails to bind or never sees the challenge traffic.
Your firewall and any cloud security group must allow both ports too. A closed port 80 is the most common reason a fresh VPS can’t get a certificate.
3. Storage
Caddy keeps certificates, private keys, and its ACME account in a data directory. For the packaged Linux service that’s /var/lib/caddy/.local/share/caddy. You can confirm the paths Caddy sees with:
caddy environ
Two requirements. The caddy user can write there. And the directory survives deployments. In containers, that means a volume.
Why does persistence matter so much? Wipe the data directory on every deploy and Caddy requests fresh certificates on every deploy. That works for a while. Then you hit the CA’s duplicate-certificate rate limit, and issuance stops for days. The data directory is state, not cache. Never delete it casually.
Watch it happen
Once all three checks pass, load the configuration and follow the log:
journalctl -u caddy -f
You’ll see Caddy obtain the certificate, with log lines naming the challenge type and the issuer. The whole thing usually takes a few seconds.
When it fails, the error names the failing step: DNS lookup, connection, or authorization. That tells you which of the three checks to redo. Don’t change the Caddyfile. Fix the environment and try again, against the staging CA from the previous lesson until it works.
Lesson completed