Automating certificate renewal without taking the service down
Let's Encrypt certificates are short-lived by design — 90 days — which means renewal isn't an occasional chore, it's a recurring operational requirement. If it's not automated and monitored, it will eventually fail silently and you'll find out when a browser shows a warning page to actual visitors.
Standalone vs webroot
Certbot's standalone authenticator spins up its own temporary web server on port 80 to answer the ACME HTTP-01 challenge. That's simple and works great for a first-time issuance on a box with nothing else running — but it requires port 80 to be free, which means stopping nginx (or whatever else is bound there) for the duration of the request:
systemctl stop nginx
certbot certonly --standalone -d example.com -d www.example.com
systemctl start nginx
That's fine for a one-off manual issuance. It's a bad idea for automated renewal, because it means your web server goes down briefly every renewal cycle, and if the certbot step fails for any reason, you've now got a service that's stopped and a script that may not reliably bring it back.
The webroot authenticator solves this by writing the challenge file to a path your already-running web server serves directly, so nothing needs to stop:
certbot certonly --webroot -w /var/www/certbot -d example.com
This needs a bit of nginx config so /.well-known/acme-challenge/ resolves to that directory before you request anything, but once it's there, renewal becomes a zero-downtime operation.
The renewal cron job isn't the whole story
Modern certbot installs typically ship a systemd timer that handles the renewal check automatically — systemctl list-timers | grep certbot will show it if so. It runs certbot renew roughly twice a day, which itself only actually renews certificates within 30 days of expiry, so most runs are no-ops. That part is usually fine out of the box.
The part people miss is the deploy hook — the step that reloads whatever's actually using the certificate after a renewal happens. Certbot renewing the certificate files on disk does nothing for a running nginx process, which has the old certificate loaded in memory and will keep serving it until reloaded.
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh
systemctl reload nginx
Make it executable (chmod +x), drop it in the deploy hooks directory, and certbot will run it automatically after any successful renewal — but only after a successful one. If a renewal fails silently for weeks (DNS changed, firewall rule got tightened, whatever), the hook never fires and you won't know until the old cert actually expires.
Actually monitoring it
A dry run tells you whether the current setup would succeed, without touching your real certificate:
certbot renew --dry-run
Running that manually every so often, or wiring it into whatever monitoring already pages you for other things, closes the gap a silent renewal failure leaves open. The whole point of automating this is removing it from your mental checklist — but automation you never verify is just a different way of hoping.
← Back to all posts