Guides Certificates

Is Certbot auto-renewal working?

Updated 7 min read

Short answer

Check three things: the timer that runs certbot renew, a dry run, and the log of the last real runs. Debian and Ubuntu packages use certbot.timer; the snap uses snap.certbot.renew.timer.

What to do: run systemctl list-timers, then sudo certbot renew --dry-run, then read the journal. The timer runs twice a day, and Certbot renews a 90-day certificate about 30 days before it expires.

Watch the certificate, not the job: Expiry Watch checks the certificate your site serves every day and emails you when its renewal looks late. Free, with no account. Watch a domain

Find the timer

Certbot’s documentation says where to look: “look for the certbot renew command in either your system’s crontab (typically /etc/crontab or /etc/cron./) or systemd timers (systemctl list-timers)” (Certbot User Guide). On Debian and Ubuntu, the package installs a timer called certbot.timer:

systemctl list-timers certbot.timer
NEXT                        LEFT LAST PASSED UNIT          ACTIVATES
Sat 2026-10-24 21:56:25 UTC  18h -         - certbot.timer certbot.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.

We captured this on October 24, 2026 in a fresh Debian 13 test container (Certbot 4.0.0, systemd 257). NEXT is when it runs again, and ACTIVATES the service that runs Certbot. On a fresh install LAST is empty; on a server that has been up for a while it shows the last run, and PASSED should be less than a day. If the command lists no timer, check systemctl is-enabled certbot.timer, which should say enabled.

The schedule is in the package’s timer file, the same on Debian 12 and 13 and on Ubuntu 24.04 (Debian’s certbot.timer):

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

That is midnight and noon, each pushed back by a random delay of up to 12 hours. Persistent=true makes up a run that was missed while the machine was off, once the timer starts again (still after a random delay) (systemd.timer).

With the snap

The snap defines the job as an app called renew that runs “approximately twice a day with randomization” (Certbot’s snapcraft.yaml). snapd names its units snap.<snap>.<app> (snapd source), so look for:

systemctl list-timers snap.certbot.renew.timer

The line looks the same, with that unit name. If you moved from the Debian or Ubuntu package to the snap, check that only one of the two timers is left.

Without systemd

Certbot installed with pip doesn’t set up a schedule. Its documentation gives this line, which adds a job to /etc/crontab for midnight and noon, offset by a random delay of up to an hour chosen when you add it:

SLEEPTIME=$(awk 'BEGIN{srand(); print int(rand()*(3600+1))}'); \
echo "0 0,12 * * * root sleep $SLEEPTIME && certbot renew -q" \
  | sudo tee -a /etc/crontab > /dev/null

Debian’s package also ships /etc/cron.d/certbot, but its line runs only when systemd isn’t running: it starts with test -x /usr/bin/certbot -a \! -d /run/systemd/system (Debian’s certbot.cron.d). On a systemd machine the timer is the one that counts.

Run a dry run

A timer that runs is half the answer; the renewal itself has to work too. --dry-run goes through a full renewal of every certificate against Let’s Encrypt’s staging server, validation included, and saves nothing:

sudo certbot renew --dry-run

When it passes, the summary reads “Congratulations, all simulated renewals succeeded” (Certbot’s renewal.py). If it doesn’t, Certbot renew failed matches each error type to its cause.

A dry run tests the renewal, not the schedule, and a plain dry run skips deploy hooks. Add --run-deploy-hooks to test those as well.

Read the last runs

The timer’s service logs to the journal. With -q, Certbot prints nothing when all is well, so a healthy run is only systemd’s lines; a failed one includes Certbot’s error. We broke a test certificate’s renewal on purpose in the same Debian container, by setting it up for a DNS plugin that wasn’t installed, and ran the service twice, before and after:

sudo journalctl -u certbot.service -n 20 --no-pager
Oct 24 02:59:35 fd59546b7a3f systemd[1]: Starting certbot.service - Certbot...
Oct 24 02:59:36 fd59546b7a3f systemd[1]: certbot.service: Deactivated successfully.
Oct 24 02:59:36 fd59546b7a3f systemd[1]: Finished certbot.service - Certbot.
Oct 24 02:59:36 fd59546b7a3f systemd[1]: certbot.service: Consumed 1.613s CPU time, 32.2M memory peak.
Oct 24 03:01:33 fd59546b7a3f systemd[1]: Starting certbot.service - Certbot...
Oct 24 03:01:34 fd59546b7a3f certbot[238]: Failed to renew certificate example.com with error: The requested dns-cloudflare plugin does not appear to be installed
Oct 24 03:01:34 fd59546b7a3f certbot[238]: All renewals failed. The following certificates could not be renewed:
Oct 24 03:01:34 fd59546b7a3f certbot[238]:   /etc/letsencrypt/live/example.com/fullchain.pem (failure)
Oct 24 03:01:34 fd59546b7a3f certbot[238]: 1 renew failure(s), 0 parse failure(s)
Oct 24 03:01:34 fd59546b7a3f systemd[1]: certbot.service: Main process exited, code=exited, status=1/FAILURE
Oct 24 03:01:34 fd59546b7a3f systemd[1]: certbot.service: Failed with result 'exit-code'.
Oct 24 03:01:34 fd59546b7a3f systemd[1]: Failed to start certbot.service - Certbot.

The first run succeeded: Finished certbot.service. The second failed: status=1/FAILURE, with Certbot’s reason two lines below Starting. A plugin can go missing this way after moving from a distribution package to the snap, which installs each DNS plugin as a snap of its own: sudo snap install certbot-dns-<PLUGIN> (Certbot’s instructions). For the snap, read the journal with -u snap.certbot.renew.service.

Certbot’s own debug log, /var/log/letsencrypt/letsencrypt.log, says what it decided for each certificate:

sudo grep -E 'due for renewal|not yet due' \
  /var/log/letsencrypt/letsencrypt.log
2026-10-24 03:01:34,319:INFO:certbot._internal.renewal:Certificate is due for renewal, auto-renewing...

On a normal day the lines say Certificate not yet due for renewal. Debian’s package rotates this log weekly and keeps 12 files (certbot.logrotate), so older runs are in letsencrypt.log.1 and the compressed files after it.

When Certbot renews

The timer runs certbot renew twice a day, but it renews only certificates that are due. Certbot’s documentation is plain about it: renew “can be run as frequently as you want - since it will usually take no action.” What counts as due depends on the version (CHANGELOG):

Certbot version Renews when
Before 4.0.0 30 days before expiry
4.0.0 30 days before expiry or a third of the lifetime left, whichever comes first (half, for certificates shorter than 10 days)
4.1.0 and later The CA’s suggested window, through ACME Renewal Info (ARI), which for Let’s Encrypt “will typically cause renewal at around 2/3rds of the certificate’s lifetime”; otherwise when a third of the lifetime is left (half under 10 days)

For a 90-day Let’s Encrypt certificate, all three land in the same place: about 30 days before it expires. A renew_before_expiry line in a certificate’s file in /etc/letsencrypt/renewal/ overrides the default, though from 4.1.0 an earlier ARI window still wins.

Which version you have depends on how you installed it, as of October 24, 2026:

Installed from Certbot version
Debian 12 package 2.1.0
Ubuntu 24.04 package 2.9.0
Debian 13 and Ubuntu 26.04 packages 4.0.0
Snap, stable channel 5.8.0

Sources: Debian’s package list, Launchpad and the Snap Store. certbot --version shows yours.

When Certbot runs outside a terminal and a certificate is due, it first waits a random time of up to 8 minutes, so that many servers don’t all contact the CA at once (renewal.py). Debian’s service turns that off with --no-random-sleep-on-renew, because its timer already adds a random delay.

Check what the server sends

A renewal that succeeds can still leave visitors with the old certificate: the web server keeps the one it loaded until it reloads. With the --nginx or --apache plugin Certbot reloads it for you; with certonly, you need a deploy hook. Site still shows the old SSL certificate shows how to add one.

The timer, the dry run and the log all describe the job. The only check that covers everything is the certificate your site actually serves, from outside: its expiry date should move forward about every 60 days. Check SSL expiry shows how to read it from a terminal.

Sources

All sources were checked on October 24, 2026. The systemd and Certbot output comes from our own runs in a Debian 13 test container on that date.

Keep reading