A Certbot certificate can renew successfully while the service that uses it keeps serving the old certificate until it is reloaded or synced. Configure a deploy hook when a command must run only after Certbot has obtained a new certificate, not after every scheduled renewal check.
A deploy hook is different from a pre or post renewal hook. Certbot runs deploy hooks once for each successfully issued or renewed certificate, and the hook receives RENEWED_LINEAGE and RENEWED_DOMAINS so the script can act on the certificate that changed.
Use a certificate-specific hook when only one lineage needs the deployment action. Host-wide directory hooks belong under /etc/letsencrypt/renewal-hooks/deploy and are better when the same script should run for every renewed certificate on the server.
Related: List Certbot certificates
Related: Test Certbot certificate renewal
Related: Configure a Certbot renewal hook
$ sudo certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Found the following certs:
Certificate Name: www.example.com
Domains: www.example.com example.com
Expiry Date: 2026-07-25 06:50:00+00:00 (VALID: 42 days)
Certificate Path: /etc/letsencrypt/live/www.example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/www.example.com/privkey.pem
Use the value after Certificate Name with --cert-name. If no certificate appears, configure the deploy hook on the host where the certificate was issued or create the certificate before adding the hook.
$ sudoedit /usr/local/sbin/deploy-certbot-web.sh
#!/bin/sh
set -eu
domains=${RENEWED_DOMAINS:-unknown}
lineage=${RENEWED_LINEAGE:-unknown}
printf 'Deploy hook ran for %s from %s\n' "$domains" "$lineage"
systemctl reload nginx
Replace systemctl reload nginx with the deployment command that must run after the renewed certificate is available. Keep the script non-interactive because automatic renewal may run from systemd or cron without a terminal.
$ sudo chmod 755 /usr/local/sbin/deploy-certbot-web.sh
$ sudo RENEWED_DOMAINS="www.example.com example.com" \ RENEWED_LINEAGE="/etc/letsencrypt/live/www.example.com" \ /usr/local/sbin/deploy-certbot-web.sh Deploy hook ran for www.example.com example.com from /etc/letsencrypt/live/www.example.com
Fix script, permission, service, or reload errors before attaching the script to Certbot. A failing hook prints an error, but the hook failure is not the same as a failed renewal.
$ sudo certbot reconfigure \ --cert-name www.example.com \ --deploy-hook /usr/local/sbin/deploy-certbot-web.sh \ --run-deploy-hooks
certbot reconfigure updates the saved renewal configuration for one certificate name. --run-deploy-hooks makes the test path run deploy hooks if the dry-run renewal succeeds, and the hook uses the current active certificate files rather than the temporary staging certificate.
$ sudo certbot renew --cert-name www.example.com --dry-run --run-deploy-hooks Saving debug log to /var/log/letsencrypt/letsencrypt.log Processing /etc/letsencrypt/renewal/www.example.com.conf Simulating renewal of an existing certificate for www.example.com and example.com Deploy hook ran for www.example.com example.com from /etc/letsencrypt/live/www.example.com Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/www.example.com/fullchain.pem (success)
During --dry-run, deploy hooks run only when --run-deploy-hooks is included. A host without a saved renewal configuration for the certificate cannot prove the hook with this command.
$ 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 deploy hook runs after Certbot actually issues or renews the matching certificate.