Certbot exited 0. OpenSSL on:443 still showed last month's cert

mlmusson1 pts0 comments

Certbot says it renewed. Your server is still serving the old cert.<br>CertPost

Sign InSign Up

Jul 29, 2026<br>Certbot says it renewed. Your server is still serving the old cert.<br>Certbot exited 0. Disk has a fresh fullchain.pem. openssl on :443 still shows last month's notAfter. Five failure modes, each with one command.

Certbot exited 0. The log says success. fullchain.pem on disk has a notAfter 89 days out.<br>openssl s_client against :443 still prints last month's date.<br>That gap is the whole problem. Disk and wire disagree. Visitors get the wire.<br>Run Step 0 once. Compare the two dates. Then walk the five modes below until they match:<br>Service never reloaded after renew<br>ACME challenge stopped validating<br>Timer or cron is gone<br>Errors went somewhere nobody reads<br>Rate limit ate the real renew<br>Each mode has one command that settles it.<br>Step 0 — check what your server is actually serving<br>Not what's on disk. Not what the certbot log says. What a visitor's browser receives right now:<br>echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \<br>| openssl x509 -noout -dates -issuer<br>Or run our free SSL check — same answer plus the chain, hostname coverage and TLS config, no signup.<br>If the notAfter date is comfortably in the future, your renewal is fine and something else scared you. If it's near or past, one of the five failures below is your problem.<br>1. Renewal succeeded — the service never reloaded<br>Certbot wrote a fresh certificate to disk, exited zero, logged success. But nginx, Apache, HAProxy or Postfix loads certificates at startup , and nothing told it to reload. It keeps serving the old cert until it expires, while every log on the box says you renewed weeks ago.<br>Compare disk to wire:<br># what's on disk<br>openssl x509 -noout -enddate -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem<br># what's being served (from step 0)<br>If they disagree, that's it. Reload the service, then make it permanent with a deploy hook so this never depends on someone remembering:<br># /etc/letsencrypt/renewal-hooks/deploy/reload.sh (chmod +x)<br>#!/bin/sh<br>systemctl reload nginx<br>We wrote up a full postmortem of this exact failure — renewed on disk, old cert on the wire, nobody the wiser.<br>Full write-up with the reload diagnosis and deploy-hook commands: nginx reloaded nothing. Certbot still exited 0.<br>2. The challenge stopped validating<br>Renewal worked for months, then silently didn't. Something changed around it:<br>HTTP-01: port 80 got firewalled, or a new redirect rule sends /.well-known/acme-challenge/ somewhere else. Common after "we forced HTTPS everywhere" tickets.<br>DNS-01: someone rotated the DNS provider's API token, migrated the zone, or tightened IAM permissions. Certbot can no longer write the TXT record. One team we read about lost a subdomain's DNS record entirely — renewal failed quietly for weeks because one name on the certificate no longer resolved.<br>The dry run tells you which without burning a real attempt:<br>certbot renew --dry-run<br>Read the error it prints for the exact domain. Fix the challenge path, not the symptom.<br>Full write-up with the curl table and the nginx and Apache carve-outs: HTTP-01 died after you forced HTTPS.<br>3. Nothing is actually running<br>Certbot installs either a cron job or a systemd timer depending on distro and install method — and it's easy to end up with neither, especially after a migration or a container rebuild.<br>systemctl list-timers | grep -i certbot<br>ls /etc/cron.d/ | grep -i certbot<br>Both empty? There's your answer. Re-enable the timer (systemctl enable --now certbot.timer) or add the cron entry, then dry-run.<br>Full write-up with the systemd, cron, snap, and container paths: The certbot timer is not running.<br>4. The errors went somewhere nobody reads<br>The renewal has been failing — loudly, even — but the noise went to a place no human looks. The classic, quoted verbatim from someone who lived it: the cron job was emailing its errors to root on a box with no mail server. Weeks of failure notices, delivered to nowhere.<br>grep -i "fail\|error" /var/log/letsencrypt/letsencrypt.log | tail -20<br>sudo mail # you might be surprised<br>If your alerting depends on local mail, a log nobody tails, or a Slack webhook that got rotated, you don't have alerting. You have a diary.<br>Full write-up with the log forensics and the off-box alert routing: Renewal failed. Root mail never left the box.<br>5. Rate limits<br>Let's Encrypt caps issuance per registered domain per week. A re-issue loop in a pipeline, a burst of new subdomains, or repeated failed-then-retried renewals can exhaust it — and then a legitimate renewal gets refused at the worst moment. The log says too many certificates already issued. The fix is to find the loop, not to retry harder; the limit resets on a rolling window.<br>Disk fine, browser angry?<br>If disk notAfter is fine but browsers warn, check chain and reload, not certbot. Run a full chain check against what your server sends — an expired or missing intermediate fails visitors while your own...

certbot disk renewal openssl still says

Related Articles