Runbook: Certificate Renewal¶
Overview¶
TLS certificates for api.engramia.dev are managed by Caddy via Let's Encrypt
ACME (HTTP-01 challenge). Caddy renews certificates automatically ~30 days before
expiry. Manual intervention is rarely needed.
Checking Certificate Status¶
# From local machine
echo | openssl s_client -connect api.engramia.dev:443 2>/dev/null \
| openssl x509 -noout -dates
# Look for: notAfter=...
# On VM — check Caddy logs
ssh root@engramia-staging \
'docker compose -f /opt/engramia/docker-compose.prod.yml logs caddy \
--since 24h | grep -i "certificate\|tls\|acme\|renew"'
If Auto-Renewal Fails¶
Step 1 — Check Caddy container is running¶
Step 2 — Verify port 80 is reachable (required for HTTP-01 challenge)¶
curl -I http://api.engramia.dev
# Must return 200 or 301 — if blocked, Let's Encrypt challenge will fail
Step 3 — Check caddy_data volume is intact¶
ssh root@engramia-staging \
'docker run --rm -v engramia_caddy_data:/data alpine ls /data/caddy/certificates'
# Should list certificates directory
Step 4 — Force Caddy to retry renewal¶
ssh root@engramia-staging '
cd /opt/engramia
docker compose -f docker-compose.prod.yml restart caddy
'
# Caddy will attempt renewal on startup if cert is near expiry
Step 5 — Manual renewal via Caddy CLI (if above fails)¶
ssh root@engramia-staging \
'docker compose -f /opt/engramia/docker-compose.prod.yml exec caddy \
caddy renew --force'
Caddyfile Reference¶
Current config at /opt/engramia/Caddyfile:
Caddy handles HTTPS automatically — no manual cert config needed.
Prevention¶
- Monitor certificate expiry: alert if
notAfter< 14 days - Ensure port 80 is open in Hetzner firewall rules
- Keep
caddy_datavolume backed up (contains ACME account + certs) - Do not use
caddy:latest— pin to a semver tag (already done indocker-compose.prod.yml)