Too many certificates already issued for this exact set of domains.

Let's Encrypt refused the renewal because something already burned the quota. How to read which limit you hit, find the loop that spent it, and get a certificate back on the wire without waiting a week.

Cover Image for Too many certificates already issued for this exact set of domains.

The renewal you actually needed is the one that got refused.

Something had been re-issuing certificates for weeks — a container that rebuilt nightly, a deploy script with --force-renewal in it, a pipeline that ran certonly on every push. None of it was visible, because every one of those runs succeeded. Then the certificate came up for real renewal, and Let's Encrypt returned an error instead of a certificate:

Error creating new order :: too many certificates (5) already issued for
this exact set of domains in the last 168 hours: yourdomain.com,
www.yourdomain.com, retry after 2026-08-09T04:11:22Z

The quota was spent by runs that did not need certificates, and now the run that does cannot have one. The leaf on :443 keeps counting down while you sort it out.

Prove it

Which limit did you hit?

The error names the limit and the retry time. Pull the recent ones out of the log:

sudo grep -iE "too many|rate ?limit|retry after|429" /var/log/letsencrypt/letsencrypt.log | tail -20

Let's Encrypt runs these as refilling buckets rather than hard weekly resets, which matters: you usually do not wait the full window, you wait for one slot to come back.

Error namesThe limitHow fast it comes back
too many certificates already issued for this exact set of domains5 certificates per identical name set per 7 daysone slot every 34 hours
too many certificates already issued for registered domain50 certificates per registered domain per 7 daysone slot every 202 minutes
too many failed authorizations5 authorization failures per name per account per hourone slot every 12 minutes
too many new orders300 new orders per account per 3 hoursone slot every 36 seconds

Two of those mean different things. The exact-set limit is a re-issue loop: the same names over and over. The registered-domain limit is breadth, and it bites multi-tenant setups issuing per-customer subdomains. The failed-authorization limit is not an issuance problem at all — it means your challenges are failing, and the retries are digging the hole deeper.

If the log shows failed authorizations rather than issued certificates, the rate limit is a symptom. Fix the challenge first: HTTP-01 died after you forced HTTPS covers the usual reason a challenge stops resolving.

What is on the wire right now?

Before changing anything, find out how much time you actually have:

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

Or read it from outside with the SSL checker, which reports the leaf your visitors are being served along with chain and hostname state.

This one number decides your next move. If the served certificate is still valid for several days, you do not have an emergency — stop the loop, wait for a slot, renew normally. If it has already expired, skip to the last fix.

Fix: find what spent the quota

certbot renew is not the culprit, and this is worth being clear about because the internet says otherwise. renew checks each certificate's expiry and does nothing unless it is inside the renewal window, so running it hourly costs nothing. Quota gets spent by commands that ask for a new certificate every time they run.

Look for those:

# certificates issued far more often than they expire
sudo ls -la --time-style=long-iso /etc/letsencrypt/archive/yourdomain.com/

# force-renewal or certonly hiding in cron, units, or deploy scripts
sudo grep -rniE "force.renewal|certonly" /etc/cron* /etc/systemd/system /usr/local/bin 2>/dev/null

If archive/ holds ten cert*.pem files from the last week, you have found the loop. The usual causes:

  • A container that does not persist /etc/letsencrypt. Every restart looks like a first run, so it issues again. Mount that path on a volume and the loop stops.
  • --force-renewal left in a script after someone used it once to fix something. It does exactly what it says, every run.
  • A CI job running certonly on every deploy instead of letting the timer handle renewals.

Fix the cause before asking for another certificate. Otherwise the fresh one gets spent the same way.

Fix: test against staging, not production

While you are debugging hooks, webroot paths, or DNS plugins, point at the staging environment. Staging issues untrusted certificates against separate, far larger quotas.

# exercises the full renewal path against staging
sudo certbot renew --dry-run

# a fresh issuance test against staging
sudo certbot certonly --webroot -w /var/www/html -d yourdomain.com --test-cert

Nothing you do with these touches your production limits. Any debugging loop belongs here.

Fix: getting a certificate back when you are already expired

If the served certificate has expired and you cannot wait for a slot, a certificate for a different set of names is a different bucket, so it is not blocked by the exact-set limit:

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

Two conditions before you try this. The extra name must genuinely validate — it needs a DNS record resolving to the same server, or the challenge fails and you start consuming the failed-authorization limit instead. And the new certificate still counts against the 50-per-registered-domain bucket, so this is a way out of one limit, not out of all of them.

The name you add is a real name on your production certificate, so pick one you are happy to serve rather than something disposable.

There is also a quieter route worth knowing. Renewals driven by ACME Renewal Information, where the client asks Let's Encrypt when to renew and follows that answer, are exempt from rate limits. Recent certbot versions use ARI automatically, so keeping the client current is itself protection against ever landing here.

Stop finding out this way

Every version of this failure was invisible until the certificate was already refused. The re-issue loop ran quietly for weeks. The quota drained quietly. The refusal only surfaced when the renewal mattered.

An outside check does not care why the certificate on :443 is old, which is what makes it catch all of these at once. CertPost reads the served certificate on a schedule and warns at 30, 14, 7, and 1 days, so a blocked renewal reaches you while there is still a week of runway instead of on the morning it expires. Three certificates free, no card.

For a one-off look right now, the SSL expiry checker reads the live expiry off port 443 or any TLS port.

One check now, or every day from now on.

3 certificates free forever · No agent · No credit card