SSL & Certs Advanced

Troubleshooting Let’s Encrypt Renewal Error: ‘Too Many Certificates for Domain’ on Ubuntu 22.04 LTS

Resolve 'Too Many Certificates' Let's Encrypt renewal errors on Ubuntu 22.04 LTS. This guide helps clear excess certs and restore automatic SSL renewals.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve 'Too Many Certificates' Let's Encrypt renewal errors on Ubuntu 22.04 LTS. This guide helps clear excess certs and restore automatic SSL renewals.

Facing the "Too Many Certificates for Domain" error during a Let's Encrypt renewal can be frustrating, especially when it leaves your website serving an expired or insecure connection. This issue typically arises when numerous certificates have been issued for the same domain within a short period, exceeding Let's Encrypt's established rate limits. As an expert Systems Administrator, I'll guide you through diagnosing the root cause, identifying redundant certificates, safely removing them, and ultimately getting your SSL renewals back on track on your Ubuntu 22.04 LTS server.

Symptom & Error Signature

When attempting to renew your Let's Encrypt certificates, either manually via certbot renew or during an automated cron job, the process fails. Your website might display an "NET::ERR_CERT_DATE_INVALID" or "Your connection is not private" error in web browsers if the existing certificate has expired.

The most common error messages you'll encounter in your terminal or /var/log/letsencrypt/letsencrypt.log will resemble the following:

$ sudo certbot renew --nginx

Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not due for renewal, but trying anyway.
Plugins selected: Authenticator nginx, Installer nginx
Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf produced an unexpected error: An error occurred while communicating with the Let's Encrypt ACMEv2 API. (Detail: urn:ietf:params:acme:error:rateLimited :: Rate limit for 'certificates per Registered Domain' exceeded.). Skipping.
All renewals failed. The following certs could not be renewed:
  /etc/letsencrypt/live/example.com/fullchain.pem (failure)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1 renew failure(s), 0 parse failure(s)
Ask for help or search for solutions at https://community.letsencrypt.org/.
See the logfile /var/log/letsencrypt/letsencrypt.log for more details.

The key phrase here is Rate limit for 'certificates per Registered Domain' exceeded.

Root Cause Analysis

The "Too Many Certificates for Domain" error is a direct consequence of hitting a specific Let's Encrypt rate limit: Certificates per Registered Domain.

Let's Encrypt imposes rate limits to ensure fair usage and prevent abuse of their free certificate service. The most relevant limit in this scenario is:

  • Certificates per Registered Domain: You can issue a maximum of 50 certificates for a given Registered Domain within a rolling 7-day window. A Registered Domain is typically the main domain name (e.g., example.com), and all its subdomains (e.g., www.example.com, blog.example.com, app.example.com) count towards this limit for that Registered Domain.

Why does this limit often get hit?

  1. Repeated certbot certonly or certbot run attempts: If certbot is invoked with certonly or run commands (instead of renew) for the same set of domains, it attempts to issue a new certificate each time. This often happens if users don't understand certbot's workflow or if automated scripts fail and retry frequently without checking for existing certificates.
  2. Misconfigured Automation: Automated scripts or cron jobs might be issuing new certificates rather than renewing existing ones, especially if they operate on a per-subdomain basis instead of managing a consolidated certificate.
  3. Testing with Real Certificates: Using certbot certonly repeatedly for testing purposes without utilizing --dry-run can quickly consume your quota.
  4. Dynamic Subdomains & Poor Management: If you frequently add or remove subdomains and issue separate certificates for each change, rather than managing a single certificate that covers all necessary (and stable) subdomains, you can hit the limit.
  5. Incorrect certbot State: Sometimes, certbot's internal configuration (in /etc/letsencrypt) can become corrupted or inconsistent, leading it to believe no certificate exists and thus attempting to issue a new one instead of renewing.

Understanding this limit is crucial. The solution involves cleaning up the redundant certificates and ensuring certbot is configured to renew existing certificates correctly.

Step-by-Step Resolution

The goal is to identify and remove the excess certificates from your certbot installation, wait for the rate limit to clear, and then successfully renew or issue a single, consolidated certificate.

1. Assess Existing Certificates

First, list all certificates certbot knows about. This helps identify duplicates or unnecessary entries.

sudo certbot certificates

Look for certificates associated with your problem domain. You might see multiple entries for the same domain or subdomains.

Example Output:

Found the following certs:
  Certificate Name: example.com-0001
    Domains: example.com www.example.com
    Expiry Date: 2026-03-20 10:30:00+00:00 (VALID: 80 days)
    Certificate Path: /etc/letsencrypt/live/example.com-0001/fullchain.pem
  Certificate Name: example.com
    Domains: example.com www.example.com
    Expiry Date: 2026-03-20 10:30:00+00:00 (VALID: 80 days)
    Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
  Certificate Name: old.example.com
    Domains: old.example.com
    Expiry Date: 2026-01-15 05:00:00+00:00 (VALID: 10 days)
    Certificate Path: /etc/letsencrypt/live/old.example.com/fullchain.pem

In this example, example.com and example.com-0001 might be duplicates. If old.example.com is no longer needed, it should also be removed.

2. Identify and Backup Certbot Configuration

Before making any changes, it's critical to back up your entire /etc/letsencrypt directory. This allows you to revert if anything goes wrong.

sudo tar -czvf /root/letsencrypt_backup_$(date +%F_%H-%M-%S).tar.gz /etc/letsencrypt

Store this backup securely. It contains your private keys and certificate configurations.

3. Delete Redundant Certificates

Carefully identify the certificates you no longer need or are duplicates. Use the Certificate Name from the certbot certificates output.

sudo certbot delete --cert-name example.com-0001
sudo certbot delete --cert-name old.example.com

You will be prompted to confirm the deletion.

Deleting the wrong certificate will immediately break HTTPS for the associated domains. Ensure you are deleting redundant or unused certificates only. If in doubt, only delete certificates with very short remaining validity or those you know for certain are duplicates.

Repeat the sudo certbot delete --cert-name <CERT_NAME> command for all superfluous certificates. After deletion, run sudo certbot certificates again to verify that only the desired certificates remain.

4. Clean Up /etc/letsencrypt/live and /etc/letsencrypt/archive (If certbot delete Fails)

In rare cases, certbot delete might fail or leave remnants. This step is a more advanced, manual cleanup and should only be performed if the previous step did not resolve the issue.

Manual deletion within /etc/letsencrypt can lead to an unrecoverable certbot state if not done correctly. Proceed with extreme caution and ensure you have a fresh backup (from step 2).

  1. Inspect live and archive directories:

    ls -l /etc/letsencrypt/live/
    ls -l /etc/letsencrypt/archive/
    

    You might see directories corresponding to the certificates you intended to delete.

  2. Manually Remove/Rename: If you find directories like example.com-0001 in /etc/letsencrypt/live/ or /etc/letsencrypt/archive/ after certbot delete, you can move them out of the way.

    sudo mv /etc/letsencrypt/live/example.com-0001 /etc/letsencrypt/live/example.com-0001.bak
    sudo mv /etc/letsencrypt/archive/example.com-0001 /etc/letsencrypt/archive/example.com-0001.bak
    

    Also, check for and remove the corresponding renewal configuration file:

    sudo rm /etc/letsencrypt/renewal/example.com-0001.conf
    

    After these manual steps, run sudo certbot certificates again to see if the internal state has updated. If not, a full certbot reinstall or more drastic manual cleanup might be required (which is beyond the scope of a typical resolution for this specific error).

5. Verify Nginx Configuration

Ensure your Nginx server block points to the correct (the one you intend to keep and renew) certificate paths. Typically, this would be /etc/letsencrypt/live/your.domain.name/fullchain.pem and privkey.pem.

Examine your Nginx configuration files, usually located in /etc/nginx/sites-available/:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    # ... other SSL directives ...
}

Test your Nginx configuration for syntax errors:

sudo nginx -t

If there are no errors, reload Nginx:

sudo systemctl reload nginx

6. Attempt Certificate Renewal/Issuance

After cleaning up the excess certificates, you need to wait for the Let's Encrypt rate limit to reset. This can be anywhere from a few minutes to up to 7 days, depending on how many certificates were issued recently and when the rolling window expires. Typically, if you've only hit the limit once, a few hours might suffice.

Once you believe the rate limit has reset, proceed with renewing your desired certificate.

Always use --dry-run first to test the process without hitting real rate limits.

If you have an existing certificate you want to renew (which should be the case after cleanup):

sudo certbot renew --dry-run

If the dry run is successful, proceed with the actual renewal:

sudo certbot renew --force-renewal

--force-renewal tells certbot to attempt renewal even if it thinks the certificate isn't due. Use this sparingly, usually only when recovering from rate limit issues.

If you deleted all certificates for a domain and need to issue a fresh one (less common in this scenario but possible):

sudo certbot --nginx -d example.com -d www.example.com --dry-run

Replace example.com and www.example.com with your actual domain(s). If the dry run is successful, remove --dry-run:

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

After a successful issuance or renewal, certbot will usually handle reloading Nginx. If not, or if you want to be certain:

sudo systemctl reload nginx

Verify your certificate is now valid by checking your website in a browser or using an online SSL checker.

7. Prevent Future Occurrences

To avoid hitting this rate limit again:

  • Always use certbot renew: This command is designed to renew existing certificates. Only use certbot certonly or certbot run when you are issuing a brand-new certificate for a domain that doesn't have one managed by certbot on that server.
  • Consolidate Domains: Whenever possible, issue a single certificate that covers all necessary subdomains (e.g., example.com, www.example.com, mail.example.com) rather than separate certificates for each. This is done by adding multiple -d flags to your certbot command: certbot --nginx -d example.com -d www.example.com -d mail.example.com.
  • Understand certbot Configuration: Get familiar with the files in /etc/letsencrypt/live, /etc/letsencrypt/archive, and /etc/letsencrypt/renewal to understand how certbot manages your certificates.
  • Utilize --dry-run for Testing: When experimenting with new certbot commands or configurations, always use --dry-run first to prevent hitting rate limits unnecessarily.
  • Review Automation: If you have custom scripts for certificate management, ensure they correctly use certbot renew and handle existing certificates gracefully.
👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.