← Recipes

阅读中文版本

Rotate TLS Certificates with an Automated Hook

Issue a certificate with an ACME client, install it behind a load balancer, and wire a post-renewal hook that reloads the server so rotation needs no human step.

~30 mintlscertbotautomation

Task

Set up automatic TLS certificate renewal so the server reloads the new certificate within minutes of issuance.

When to use this

A certificate expired on Sunday morning because nobody owned the renewal email. The team wants renewal automatic and the reload wired to the renewal event, not a calendar reminder.

Prerequisites

  • A domain with DNS pointing at your server
  • An ACME account with certbot or lego
  • A reload command that does not drop in-flight requests

Steps

1. Request the first certificate with a deploy hook that reloads the server.

certbot certonly --webroot -w /var/www -d example.com --deploy-hook "systemctl reload nginx"

Expected output: A certificate is issued under /etc/letsencrypt/live/example.com and nginx reloads without dropping connections.

2. Schedule the renewal timer so it checks twice a day and runs the deploy hook on every successful renewal.

[Timer]
OnCalendar=*-*-* 00,12:00:00
Persistent=true

Expected output: systemctl list-timers shows certbot.timer active, and the next renewal triggers the reload hook automatically.

3. Test the hook by forcing a renewal in dry-run mode and confirming the reload command runs.

certbot renew --dry-run

Expected output: The dry run reports a simulated successful renewal and the deploy hook logs a reload event without changing the live certificate.

Troubleshooting

Problem

The reload command drops in-flight requests

Solution

Use nginx -s reload or the equivalent graceful reload for your server, which rebinds without closing active connections.

Problem

The ACME challenge fails because the webroot is not served

Solution

Serve the .well-known/acme-challenge directory from the same server that certbot runs on, or switch to the DNS-01 challenge.

Limitations

  • Rate limits on the ACME server cap how often you can request a real certificate; test with --dry-run.
  • A failed renewal that nobody notices still leads to an expiry; monitor the certificate expiry as a separate alert.

Renew, then reload

The certificate is the easy part; the reload is what people forget. Wire the reload into the deploy hook so every successful renewal triggers it, and schedule the timer to check twice a day.

[Timer]
OnCalendar=*-*-* 00,12:00:00
Persistent=true

Test without spending a rate limit

Run certbot renew –dry-run to simulate the renewal and confirm the hook fires. The dry run does not change the live certificate, so you can run it as often as you like while you tune the reload command.

certbot renew --dry-run

Expected outcome

The timer renews the certificate before it expires, the deploy hook reloads the server without dropping connections, and a dry run confirms the whole path works. Monitor the certificate expiry separately, so a failed renewal alerts you well before the deadline.