Why Auto-Renewal Matters

Let's Encrypt certificates are free, but they expire after just 90 days. If you issue a certificate and forget about it, your visitors will eventually hit the dreaded "Your connection is not private" warning — and most of them will leave. The fix is simple: let Certbot renew your certificates automatically. When configured correctly, renewal happens in the background and your certificate never expires.

This guide walks you through installing Certbot on Ubuntu 24.04 with Nginx, issuing your first certificate, confirming the built-in auto-renew timer works, and testing the renewal before it matters. You'll also set up a deploy hook so Nginx reloads after every renewal, and learn how to monitor expiry so nothing ever surprises you.

This tutorial is for anyone running a website on a Ubuntu VPS or dedicated server with Nginx. If you use Apache, the steps are nearly identical — swap the Nginx plugin for the Apache one (see the FAQs at the end).

Prerequisites

  • A server running Ubuntu 24.04 (22.04 works the same) with sudo or root access.
  • Nginx installed and serving your site over HTTP.
  • A domain name whose DNS A record points to your server's IP. Verify it with:
    dig +short yourdomain.com
    The returned IP must match your server.
  • Port 80 open in your firewall. Let's Encrypt must reach your server over HTTP to verify you control the domain.
  • An email address for expiry notices (Let's Encrypt sends warnings if renewal ever fails).

Step 1 — Install Certbot and the Nginx Plugin

Ubuntu 24.04 ships Certbot in its default repositories. Install it together with the Nginx plugin, which handles both certificate issuance and Nginx configuration:

sudo apt update
sudo apt install -y certbot python3-certbot-nginx

The first command refreshes the package list; the second installs certbot and the python3-certbot-nginx plugin, which knows how to edit Nginx server blocks and install certificates for you.

Step 2 — Open Ports 80 and 443 in the Firewall

Certificate issuance requires HTTP (port 80) for the domain validation challenge, and your site needs HTTPS (port 443) afterwards. If you use UFW:

sudo ufw allow 'Nginx Full'
sudo ufw status

ufw allow 'Nginx Full' opens both ports 80 and 443 through the firewall. Run ufw status to confirm the rules are active before continuing.

Step 3 — Issue Your First Certificate

Run Certbot with the Nginx plugin, listing every domain the certificate should cover (include the www version if you serve it):

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot will ask for an email address (for expiry reminders) and whether you agree to the terms of service. Then it performs the HTTP-01 challenge: it places a token in your webroot, Let's Encrypt fetches it over port 80, and — if it matches — the certificate is issued. The Nginx plugin then automatically rewrites your server blocks to serve HTTPS and redirects HTTP to HTTPS.

Tip: If you only want the certificate and prefer to edit Nginx yourself, use certbot --nginx --no-redirect or the certonly mode. The automatic redirect is fine for most sites.

Step 4 — Confirm the Auto-Renew Timer Is Active

This is the part most people never check. On Ubuntu, the Certbot package installs a systemd timer that runs certbot -q renew twice a day. Verify it exists and is enabled:

systemctl status certbot.timer
systemctl list-timers | grep certbot

systemctl status certbot.timer shows whether the timer is loaded and active. list-timers shows the next scheduled run. You should see something like "n/a n/a ... certbot.service" scheduled twice daily. The renew subcommand only renews certificates with fewer than 30 days left, so the twice-daily runs are cheap and safe.

If the timer is missing or inactive (rare on Ubuntu 24.04), enable it with:

sudo systemctl enable --now certbot.timer

Step 5 — Test the Renewal with a Dry Run

Never trust a renewal setup you haven't tested. A dry run performs the entire renewal process against Let's Encrypt's staging servers without touching your real certificates or counting against rate limits:

sudo certbot renew --dry-run

Watch the output. Every certificate should end with The dry run was successful. If any certificate fails here, fix the underlying issue now (firewall, DNS, or webroot permissions) — not 89 days later when your site goes down.

Step 6 — Add a Deploy Hook So Nginx Reloads After Renewal

Renewal updates the certificate files, but Nginx keeps the old certificate in memory until it reloads. Attach a deploy hook that runs automatically after every successful renewal:

sudo certbot reconfigure --cert-name yourdomain.com --deploy-hook "systemctl reload nginx"

certbot reconfigure (available in Certbot 2.x, which Ubuntu 24.04 ships) rewrites the renewal configuration for yourdomain.com without reissuing anything. The --deploy-hook value is stored and executed after each future renewal, reloading Nginx gracefully — existing connections are not dropped.

Verify the hook was saved:

sudo grep -i hook /etc/letsencrypt/renewal/yourdomain.com.conf

Step 7 — Set Up Expiry Monitoring

Auto-renewal is reliable, but a belt-and-suspenders check takes two minutes. First, see all your certificates and their expiry dates at a glance:

sudo certbot certificates

To check the live certificate your visitors actually receive, query it directly:

echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates

This prints notBefore and notAfter dates of the currently served certificate. For alerting, add a weekly cron job that emails you when a certificate has fewer than 21 days left:

crontab -e

Add this line (replace the email address):

0 9 * * 1 /usr/bin/certbot certificates 2>/dev/null | grep -q "INVALID" && echo "Check SSL certificates" | mail -s "SSL expiry warning" you@example.com

This runs every Monday at 9 AM. certbot certificates marks any certificate with under 30 days remaining as INVALID, and the && chain only sends mail in that case. You'll hear from Let's Encrypt's own expiry bot too, as a second safety net.

Verify It Worked

  • systemctl is-active certbot.timer returns active.
  • sudo certbot renew --dry-run reports success for every domain.
  • Your site loads at https://yourdomain.com with a valid padlock; curl -I http://yourdomain.com returns a 301 redirect to HTTPS.
  • sudo grep -i hook /etc/letsencrypt/renewal/yourdomain.com.conf shows your deploy hook.
  • openssl expiry check shows a notAfter date roughly 90 days in the future.

Common Issues & Fixes

1. "Failed to connect" / timeout during the HTTP-01 challenge

Cause: Port 80 is blocked or DNS doesn't point to this server. Fix: Confirm the firewall rule (sudo ufw status), verify DNS with dig +short yourdomain.com, and check that no CDN or proxy is intercepting port 80. Retest with the dry run.

2. Renewal works manually but the timer never fires

Cause: The systemd timer is disabled or masked. Fix: Run sudo systemctl enable --now certbot.timer, then confirm with systemctl list-timers | grep certbot. On older systems migrated from cron, remove any leftover /etc/cron.d/certbot entry to avoid double renewals.

3. Site still serves the old certificate after renewal

Cause: Nginx was never reloaded. Fix: Add the deploy hook from Step 6, then reload once manually: sudo systemctl reload nginx. Re-check the served certificate with the openssl s_client command.

4. "Too many certificates already issued" (rate limit)

Cause: Let's Encrypt allows 5 duplicate certificates per 7 days per exact domain set. Fix: Stop reissuing and wait, or use --staging while experimenting: sudo certbot --staging --nginx -d yourdomain.com. Always dry-run before real issuance.

5. Dry run fails but the real certificate was issued fine

Cause: Something changed since issuance — a firewall rule tightened, DNS moved, or the webroot was deleted. Fix: Treat a failing dry run as a real warning: re-check ports 80/443, DNS, and that Nginx is running (systemctl status nginx). Renewal will fail exactly the same way the dry run did.


Conclusion

With Certbot installed, the systemd timer enabled, a passing dry run, and a deploy hook reloading Nginx, your SSL certificates will renew themselves forever — the twice-daily timer checks every certificate and renews anything with fewer than 30 days left. Add the weekly expiry email, and an expired certificate becomes something that simply never happens to you again.

Need help securing a fleet of servers, automating deployments, or hardening a production VPS? XpertBees handles server setup, SSL automation, and DevOps for businesses — reach out and we'll take it off your plate.