Let's Encrypt certificates expire every 90 days, but Certbot's renew
command automatically handles renewal when certificates have less than 30 days remaining. The key is setting up a cron job that runs frequently enough to catch renewals while avoiding unnecessary server load.
While your monthly approach works, we can optimize it further:
0 0 */15 * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2"
This runs every 15 days with improvements:
--quiet
suppresses output unless there's an error--post-hook
ensures Apache reloads only if renewal succeeds- Full path to certbot prevents PATH issues
For production systems, consider adding these components:
0 3 */10 * * /usr/bin/certbot renew \
--quiet \
--pre-hook "systemctl stop apache2" \
--post-hook "systemctl start apache2" \
--deploy-hook "/usr/local/bin/notify_admin.sh"
Add logging to track renewal attempts:
30 2 * * * /usr/bin/certbot renew >> /var/log/le-renew.log 2>&1
Then set up log rotation in /etc/logrotate.d/le-renew
:
/var/log/le-renew.log {
weekly
rotate 12
compress
missingok
notifempty
}
Remember cron uses system timezone. For UTC systems, schedule renewals during low-traffic periods:
0 5 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2"
Test your cron setup with:
sudo certbot renew --dry-run
Check logs afterward:
journalctl -u certbot -n 50 --no-pager
For newer Ubuntu versions (18.04+), consider systemd timers:
[Unit]
Description=Certbot Renewal
[Timer]
OnCalendar=*-*-1,15 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Let's Encrypt certificates expire every 90 days, but Certbot's renew
command is designed to handle automatic renewals when executed regularly. The key is setting up a proper cron schedule that:
- Runs frequently enough to prevent expiration (recommended twice daily)
- Includes proper error handling
- Triggers web server reload only when renewal actually occurs
While your proposed solution works technically, @monthly
scheduling creates unnecessary risks:
@monthly letsencrypt renew && service apache2 reload
Issues with this approach:
- If renewal fails, you might not notice for 30 days
- Forces Apache reload even when no renewal occurred
- Doesn't capture output for debugging
Here's the improved version I use in production:
0 3,15 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2"
Key improvements:
- Runs twice daily (3AM and 3PM) for redundancy
- Uses
--post-hook
to only reload Apache when needed --quiet
flag prevents unnecessary cron emails- Full path to certbot prevents PATH issues
For better monitoring, add logging:
0 3,15 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload apache2" >> /var/log/le-renew.log 2>&1
Then set up log rotation in /etc/logrotate.d/le-renew
:
/var/log/le-renew.log {
weekly
missingok
rotate 12
compress
delaycompress
notifempty
}
Always test your renewal process with:
sudo certbot renew --dry-run
This verifies your configuration without actually issuing certificates.
For complex setups, use separate hook scripts:
0 3,15 * * * /usr/bin/certbot renew \
--pre-hook "systemctl stop apache2" \
--post-hook "systemctl start apache2" \
--quiet
This ensures clean certificate updates for services requiring full restarts.