Optimal Cron Job Setup for Let’s Encrypt (Certbot) Renewal on Apache2: Best Practices & Examples


3 views

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.