Nginx SSL Certificate Not Renewing Automatically
When an SSL certificate managed by Let's Encrypt (via Certbot) fails to renew automatically, it's almost always because the scheduled renewal job either isn't running at all, or is silently failing on a step like domain validation — not because of an issue with Nginx's SSL configuration itself.
The Problem
Your site's certificate expires despite Certbot supposedly being set up to auto-renew, and visitors start seeing browser warnings:
NET::ERR_CERT_DATE_INVALID
Your connection is not private
Checking the certificate confirms it's genuinely expired, even though you remember configuring automatic renewal when you first set things up.
Why It Happens
Certbot's automatic renewal depends on a scheduled task actually running and completing successfully, which can silently fail for several reasons:
- The systemd timer or cron job that's supposed to run
certbot renewwas never actually installed, or was removed during a system update - The renewal process depends on Nginx being able to serve a validation file at a specific path, and a config change since the initial setup now blocks or redirects that path before Certbot's validation can complete
- The server's clock has drifted significantly, causing certificate validation to fail due to timestamp mismatches
- Renewal only runs when the certificate is within 30 days of expiring — if the scheduled job silently failed for months without anyone noticing, the certificate can expire despite an apparently correct setup
The Fix
First, confirm whether the renewal timer is actually active on your system:
systemctl status certbot.timer
If it shows as inactive or doesn't exist, enable it:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
On systems using cron instead of systemd timers, check whether the cron job exists:
sudo crontab -l
cat /etc/cron.d/certbot
Next, run a dry-run renewal manually to see the actual error, rather than waiting for the next scheduled attempt:
sudo certbot renew --dry-run
This simulates the renewal process without actually changing your certificate, and will surface the specific failure — commonly a validation path issue. If you're using the HTTP-01 challenge method, confirm your Nginx config isn't blocking or redirecting the .well-known/acme-challenge/ path before Certbot can serve its validation file:
location /.well-known/acme-challenge/ {
root /var/www/html;
}
Make sure this location block exists and isn't being caught by an earlier, broader redirect rule (like a blanket HTTP-to-HTTPS redirect) that would prevent the plain HTTP validation request from ever reaching this path.
After confirming the dry run succeeds, force an actual renewal to update the certificate immediately rather than waiting:
sudo certbot renew --force-renewal
sudo systemctl reload nginx
Still Not Working?
If the dry run fails with a DNS or connectivity-related error rather than a configuration issue, check whether your domain's DNS records changed since the certificate was first issued — for example, pointing to a new server or load balancer that Certbot's validation can't reach the same way it could originally. Also confirm that port 80 is genuinely open and reachable from the public internet, since HTTP-01 validation requires it even if your site otherwise only serves traffic over HTTPS.
If you're running Certbot inside a Docker container rather than directly on the host, the renewal timer running inside the container needs to actually persist and execute on schedule, which depends on the container itself staying up continuously — a container that gets recreated without preserving its scheduled task setup will silently lose the renewal schedule even though the certificate files themselves might still be present in a mounted volume. In containerized setups, it's often more reliable to run the renewal as a separate scheduled job on the host (a host-level cron entry calling docker exec into the Certbot container) rather than relying on a scheduler running inside a container that may not always be alive.
It's also worth setting up a simple external monitor for certificate expiration, independent of whether renewal succeeds — services that check your certificate's expiration date periodically and alert you via email give you a safety net that catches this exact failure mode weeks in advance, rather than finding out only when visitors start reporting browser warnings.