2026-11-01
Certbot renewal failed: how to diagnose and fix it
- ssl
- tls
- certificates
- debugging
- monitoring
When Certbot renewal fails, start with the error for the affected certificate. Check whether the scheduled job ran, then use a staging dry run to find the validation or plugin problem. After renewal, compare the local file with the certificate your public hostname serves. Your server may still be serving the old certificate.
This walkthrough assumes you administer the server. Replace example names and paths with your own. Start with the failure you actually observed:
| Symptom | First place to look |
|---|---|
| No recent renewal attempt | Timer or cron history and the command it runs |
| Challenge timeout or unexpected response | Public DNS, challenge routing and reachability |
| Missing plugin or credentials | The Certbot installation used by the scheduled job |
| Certificate is not due for renewal | Its dates and Certbot's renewal eligibility |
| New certificate on disk, old one online | Certificate deployment and the TLS listener |
1. Find the certificate and the failed attempt
List the certificates this Certbot installation manages:
Note the certificate name, domains, expiry and certificate path. The name may have a suffix, such as example.com-0001. Use that exact name in the commands below.
Open the log file named in the failed command's output. With the default directories, read its latest entries with:
Look for the error for the affected domain, rather than just the final failure summary. If a certificate covers several names, validation can fail for just one of them.
If there's no record of an attempt, check the scheduler. On a systemd host:
Find the matching service and read its execution history and command. Unit names depend on how you installed Certbot. For cron, check the system or user crontab that runs renewal. Make sure the job uses the binary, configuration directory and environment you expect. Certbot's automated-renewal documentation explains where to find these schedules.
2. Test the affected certificate with a dry run
Use the certificate name from the listing:
By default, Certbot tests against Let's Encrypt staging and doesn't save the test certificate. Check any custom --server setting first. Pre/post hooks still run, and Certbot may temporarily reload the web server. Deploy hooks run only if you add --run-deploy-hooks. Review these side effects before testing on a production host; Certbot's dry-run option documents them.
A normal renew command can exit successfully because no certificate was due. Read the log and certificate dates to see whether Certbot issued anything. Fix the cause and test it before retrying in production; repeated --force-renewal attempts can hit the issuer's rate limits.
3. Fix the HTTP challenge path
For HTTP-01, Let's Encrypt must retrieve a token under http://example.com/.well-known/acme-challenge/ on public port 80. A working HTTPS homepage does not test that route. Its challenge documentation specifies the port and redirect behavior.
If the request times out, check firewall, NAT and load-balancer routing. With a 404 or an unexpected response, look at the virtual host, authentication rules and webroot for the challenge path. Every frontend that can receive validation traffic needs to return the correct response.
Check both address families, using dig if available or the DNS lookup tool:
Let's Encrypt prefers IPv6 for the initial validation connection. An IPv6 server returning the wrong response does not trigger an IPv4 retry. Correct an obsolete AAAA record or make that server answer the challenge too.
The standalone plugin needs to bind port 80 itself. If your web server already uses that port, choose another validation method or plan for the interruption needed by stop/start hooks. The webroot plugin writes challenge files beneath the configured webroot, so check that your server still exposes that directory.
4. Check DNS validation and plugin access
For DNS-01, inspect the TXT record at _acme-challenge.example.com on the authoritative DNS servers. Confirm the plugin updated the right zone and allowed enough propagation time. Let's Encrypt's DNS-01 documentation describes the record and delegation behavior.
If the error names a missing DNS plugin or unreadable credentials file, check the installation and filesystem visible to the scheduled process. A plugin installed in another Python environment, or credentials mounted only in your interactive container, will not help that job.
Certificates obtained with --manual need authentication hooks for unattended renewal. An interactive TXT-record prompt cannot complete itself in cron. Use an automated DNS plugin or tested authentication hooks suited to your DNS provider.
5. Save corrected renewal settings
Certbot reuses the original plugin and options at renewal. If you've moved the webroot or changed the plugin configuration, save those changes for future runs as well as testing them once.
For Certbot 2.3.0 and newer, this example updates an existing webroot configuration:
Use the actual certificate name and webroot. reconfigure tests the new settings against staging before saving them. Follow the version-specific renewal configuration instructions for older versions. Hand-editing renewal files can break future runs.
Once the test passes, run sudo certbot renew --cert-name example.com when the certificate is due. Check the result for that certificate; the dry run hasn't replaced it in production.
6. Verify the certificate customers receive
After a successful production renewal, compare the local leaf certificate with the one served by your public endpoint. Use the certificate path reported in step 1:
|
Use the dates and leaf fingerprint to compare the certificates. That comparison doesn't establish hostname or trust-chain validity. You can also inspect a public endpoint with the SSL checker. For a local certificate, paste only its public certificate PEM into the certificate decoder.
If this endpoint should serve the local certificate but the fingerprints differ, check the listener's file path, container mounts and deployment hook. Nginx keeps its old configuration if a reload fails, so inspect the reload result too.
A CDN may legitimately serve a different certificate from your origin. Verify each TLS termination point against its intended certificate and repeat the original HTTPS request with normal verification enabled.
Set up expiry alerts
Follow the SSL expiry monitoring walkthrough to alert on the certificate customers receive and test notification delivery. For help comparing certificate fields, use the PEM-reading guide. Before closing the incident, confirm the scheduler's next run and the public endpoint's renewed expiry date.