Certbot renewal hooks cover the local actions that need to happen around certificate renewal. Configure a host-wide hook when the same action should apply to every certificate lineage on the server, such as stopping a listener before standalone validation or reloading a service after a renewed certificate is available.

Directory hooks live under Certbot's renewal-hooks tree and are separated by phase. The pre phase runs before a renewal attempt, the deploy phase runs after a successful issue or renewal, and the post phase runs after an attempted renewal finishes.

Use a deploy hook for service reloads because certbot renew can exit successfully when no certificate was close enough to expiry to renew. A dry-run renewal tests the ACME path, but deploy-hook behavior must be explicitly included in the verification command.

Steps to configure a Certbot renewal hook:

  1. Choose the hook phase for the action.
    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 issue or 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 a reload that should happen only when certificate files changed, use a deploy hook instead of checking the exit status from certbot renew.

  2. Create the deploy hook directory if it is missing.
    $ sudo install -d -m 755 /etc/letsencrypt/renewal-hooks/deploy
  3. Create the hook script.
    $ sudoedit /etc/letsencrypt/renewal-hooks/deploy/10-reload-nginx.sh
    #!/bin/sh
    set -eu
    
    printf 'Reloading nginx after renewal for %s\n' "${RENEWED_DOMAINS:-unknown}"
    systemctl reload nginx

    Replace systemctl reload nginx with the command that must run after a renewed certificate is available. Keep the script non-interactive because scheduled renewal may run from systemd or cron with no terminal.
    Related: Reload Nginx after Certbot renewal

  4. Make the hook executable.
    $ sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/10-reload-nginx.sh
  5. Confirm that Certbot can see the executable deploy hook.
    $ sudo find /etc/letsencrypt/renewal-hooks/deploy -maxdepth 1 -type f -perm -111 -print
    /etc/letsencrypt/renewal-hooks/deploy/10-reload-nginx.sh

    Certbot runs executable directory hooks in byte-sorted filename order. Use prefixes such as 10-reload-nginx.sh and 20-sync-certs.sh when more than one hook must run in a specific sequence.

  6. Run the hook script once with sample renewal variables.
    $ sudo env RENEWED_DOMAINS="www.example.com example.com" \
      /etc/letsencrypt/renewal-hooks/deploy/10-reload-nginx.sh
    Reloading nginx after renewal for www.example.com example.com

    Fix script, permission, service, or reload errors before relying on unattended renewal. A hook error is printed by Certbot, but the hook failure is not the same signal as a failed certificate renewal.

  7. Run a renewal dry run and include deploy hooks in the test.
    $ sudo certbot renew --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
    Reloading nginx after renewal for www.example.com example.com
    
    Congratulations, all simulated renewals succeeded:
      /etc/letsencrypt/live/www.example.com/fullchain.pem (success)

    During --dry-run, pre and post hooks run by default when a renewal attempt happens. Deploy hooks 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., test on a host that already has a saved renewal configuration for the certificate lineage.

  8. Confirm that the normal renewal schedule will call the hook later.
    $ systemctl list-timers --all '*certbot*'
    NEXT                        LEFT     LAST                        PASSED  UNIT          ACTIVATES
    Fri 2026-06-12 12:33:10 UTC 4h 12min Fri 2026-06-12 00:18:42 UTC 8h ago certbot.timer certbot.service

    The hook runs when the packaged renewal timer, cron job, or snap-managed schedule invokes certbot renew and a matching renewal phase occurs.
    Related: Check the Certbot renewal timer