Certbot renewal hooks run local scripts when a certificate renewal reaches a defined phase. Configure one when a renewed certificate must trigger a service reload, file sync, or deployment step so the new certificate is actually used after Certbot updates the live certificate files.
Directory hooks are the simplest durable place for host-wide renewal behavior. Certbot checks the hook directories under its configuration directory, runs executable files in byte-sorted filename order, and keeps command-line or saved renewal hooks separate from those directory files.
A deploy hook fits service reloads because it runs only after a successful renewal. Use a pre and post pair only when Certbot must temporarily stop a service before validation and start it again after the attempt.
Related: Test Certbot certificate renewal
Related: Check the Certbot renewal timer
Related: Configure a Certbot deploy hook
| Hook directory | When it runs | Use it for |
|---|---|---|
| /etc/letsencrypt/renewal-hooks/pre | Before a renewal attempt that will actually run | Temporarily stopping a service that conflicts with validation, such as a standalone listener on port 80. |
| /etc/letsencrypt/renewal-hooks/deploy | After a successful renewal | Reloading a web server, syncing renewed certificate files, or notifying a dependent service. |
| /etc/letsencrypt/renewal-hooks/post | After a renewal attempt finishes | Starting a service that was stopped by a matching pre hook, even if renewal failed. |
For service reloads, prefer a deploy hook. A plain certbot renew exit status can be 0 when no certificate needed renewal, so it is not enough proof that a certificate changed.
$ sudo install -d -m 755 /etc/letsencrypt/renewal-hooks/deploy
$ sudoedit /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/sh set -eu systemctl reload nginx
Replace systemctl reload nginx with the service command that must run after a renewed certificate is available. Keep the hook non-interactive because automatic renewal may run from systemd or cron with no terminal.
A hook that exits with an error is printed in Certbot output, but Certbot still attempts the renewal. Test destructive or restart-heavy commands by hand before leaving them in a renewal hook.
$ sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
$ sudo find /etc/letsencrypt/renewal-hooks/deploy -maxdepth 1 -type f -perm -111 -print /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
If more than one hook must run in a specific order, prefix filenames with sortable numbers such as 10-reload-nginx.sh and 20-sync-certs.sh.
$ sudo systemctl reload nginx
For Nginx, run sudo nginx -t before reloading if the hook reloads a service after configuration changes. For another service, use that service's own syntax or health check before placing the command in the hook.
$ sudo certbot renew --dry-run --run-deploy-hooks Saving debug log to /var/log/letsencrypt/letsencrypt.log Processing /etc/letsencrypt/renewal/example.com.conf Simulating renewal of an existing certificate for example.com Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/example.com/fullchain.pem (success)
During --dry-run, Certbot runs pre and post hooks by default. Deploy hooks run during a dry run only when --run-deploy-hooks is included, and they use the currently active certificate files rather than the temporary staging certificate.
If the output says No simulated renewals were attempted., the host does not have a renewal configuration that Certbot can test. Run the dry run on the server that already owns the certificate lineage.
$ sudo certbot renew Saving debug log to /var/log/letsencrypt/letsencrypt.log - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No renewals were attempted. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
A normal renewal check may do nothing when certificates are not near expiry. The hook runs when Certbot actually obtains or renews a certificate in the matching phase.