The limits people forget exist
Let's Encrypt has issued billions of certificates. For most of that history the rate limits just sat there in the background. You'd bump into them maybe once, usually in some staging environment where a CI job re-issued the same cert forty times before anyone noticed. You'd read the docs, fix the loop, and forget the limits existed. Rinse, repeat, years go by.
That's ending. Not because Let's Encrypt is touching the limits, but because CA/Browser Forum ballot SC-081v3 is dragging maximum certificate lifetimes from 398 days down toward 47 by March 2029, with a 200-day cap already live as of March 2026. Shorter certs mean more renewals. More renewals mean you start scraping against issuance ceilings that felt generous at 90 days and get claustrophobic at 47.
Time to actually learn where the walls are.
Let's Encrypt enforces a handful of limits. The ones that matter for renewal planning:
- Certificates per registered domain: 50 per week. This counts against the registered domain, not the hostname.
api.example.com,www.example.com, and a wildcard*.example.comall drink from the same 50-a-week bucket forexample.com. Public suffixes on the PSL get their own bucket. Your own apex does not. - Duplicate certificates: 5 per week. A "duplicate" is a cert covering the exact same set of names. Same SANs, any order, same certificate. Renew an identical name set six times in seven days and the sixth gets refused.
- New orders per account: 300 per 3 hours. Rarely the thing that bites, unless you're running one account across a big estate.
- Failed validations: 5 per hostname per account per hour. This is the one that ruins your incident. More on it below.
Other ACME CAs draw the lines differently, and that's the useful part. Google Trust Services runs per-project quotas through the GCP console instead of a fixed public number, and you can request bumps like any other quota. ZeroSSL ties ACME issuance to plan tier rather than a rolling weekly window, which reshapes the ceiling entirely: you're capped by contract, not by a sliding seven-day count. That difference is exactly why a second CA is worth having. The two ceilings fail for unrelated reasons.
The renewal-frequency multiplier
Here's the math almost nobody bothers to do.
At a 90-day lifetime, the standard advice, and certbot's default, is to renew at the two-thirds mark, around day 60. Call it a renewal every 60 days. Roughly six issuances per name set per year.
At 47 days you can't just renew at day 31 and call it safe. The whole point of the shorter window is a tighter margin, so most tooling targets renewal around the one-third-remaining mark, call it day 30 to 32. That's an issuance every ~30 days, or about twelve per name set per year. You've doubled issuance volume before you've done anything clever. Make your tooling more conservative, renewing early to leave slack for retries, and it drifts toward triple.
Now scale it. Say example.com has a wildcard *.example.com plus separate certs for eight services that, for one reason or another, can't hide behind the wildcard: different key types, a different deployment target, a couple of multi-SAN certs bundling three or four hostnames each. Ten distinct certificates under one registered domain.
At 90/60, that's ten certs times roughly six issuances, smeared across 52 weeks. You're not remotely near 50 in any given week. You'd have to force nearly every renewal into the same seven days to even feel the limit breathing.
At 47/30, it's ten certs times twelve. 120 issuances a year. Still fine on average. But renewals don't average. They clump. If half your certs got provisioned in the same sprint, they renew in the same week, every cycle, forever, until something knocks them out of phase. Layer in early-renewal-on-failure retries, where every failed attempt that eventually succeeds is one more issuance against the bucket, and a bad week stacks real load on top of the scheduled load. The 50-a-week ceiling that was invisible at 90 days is suddenly a number you can see from where you're standing.
The duplicate limit gets meaner too. At monthly renewal you touch each identical name set about once a month, one of five slots, comfortable. Then a retry storm re-attempts the same name set a dozen times in an afternoon and you've torched all five duplicate slots on one cert before lunch. The legitimate renewal that actually needed to go through? Refused.
The failure mode nobody plans for
Picture the incident. Your DNS provider has a bad hour, or the deploy pipeline ships a broken config that stops serving the HTTP-01 challenge. Renewal cron fires. Validation fails. Your tooling, trying to help, retries. Fails. Retries. Fails.
Now you've burned the failed-validation limit, 5 per hostname per account per hour, and the CA has stopped taking authorization attempts for that hostname until the hour rolls over. The cert expiring in three days can't be reissued. Not because the original problem is still broken, but because your own retries slammed the door on top of it.
That's the trap short lifetimes set. At 90 days a validation outage was a shrug; you had weeks of runway. At 47 days with renewal at day 30, you've got maybe two weeks of margin, and a retry storm can eat an hour of it in minutes while the clock keeps ticking.
The fix is boring and it's the whole game: know what kind of error you got, and back off accordingly. ACME hands you structured problem documents, and the type field tells you precisely what happened. A urn:ietf:params:acme:error:rateLimited is nothing like a dns or connection or unauthorized, and your tooling absolutely must not treat them as the same event. Rate-limited means stop. Retrying accomplishes exactly nothing except keeping the lock engaged, and on the failed-validation limit each retry can extend it. A connection or DNS error is transient and earns a bounded retry with backoff. An unauthorized means your challenge response is genuinely wrong, retrying will never fix it, and a human needs to look.
Let's Encrypt also sends a Retry-After header on rate-limited responses. Honor it. Exponential backoff that ignores that header is just a slower route to the same wall. And cap total attempts, always. A retry loop with no ceiling is the machine that turns a five-minute DNS blip into an hour of lockout.
Staying under the ceiling
A few moves keep issuance volume off the wall.
Consolidate SANs. Every distinct name set is its own duplicate bucket and its own renewal cadence. Eight services on eight single-name certs is eight renewal streams churning on their own schedules. Where your topology allows it, bundling hostnames onto multi-SAN certs cuts the number of distinct certificates, and with it the number of independent renewal events pulling on the per-domain limit. The tradeoff is real: a fat SAN list means one failed renewal takes out more services, and you rebuild the whole cert to add or drop a name. I'd still take it. For pure churn reduction it's the single biggest lever you've got.
Spread load across accounts and CAs. On Let's Encrypt, issuance limits are per registered domain but failed-validation limits are per account. So splitting issuance across ACME accounts spreads the failed-validation blast radius, and one hostname's retry storm no longer locks a shared account. Spreading across CAs, some certs from Let's Encrypt, some from Google Trust Services or ZeroSSL, means no single CA's ceiling is your ceiling.
Use ARI-driven jitter instead of synchronized cron. The ACME Renewal Information extension (RFC 9773) lets the CA tell you when to renew, handing you a suggested window with randomization baked in. It exists specifically to kill the thundering herd, where every cert born in the same sprint renews in the same hour until the heat death of the universe. If your client supports ARI, and recent certbot plus most actively maintained clients do, turn it on. It smears renewals across the window instead of firing them all at the top of the hour, which flattens your weekly peaks and keeps you clear of the per-domain ceiling. Bonus: a CA can also use ARI to nudge you into early renewal during a mass-revocation event. Second reason to wire it up.
Treat a second CA as an availability requirement, not a nice-to-have. This is the actual mindset shift. At 90 days, one CA having a rough day was survivable; you had weeks to wait it out. At 47 days, with a two-week window and a CA that could rate-limit you, revoke en masse, or just be down while you're trying to renew, single-CA issuance is a single point of failure. A configured fallback CA is the difference between a degraded hour and an expired cert serving production at 2am. I've been on the 2am end of that. Pick the degraded hour.
Know your issuance rate before you hit the wall
You can't manage a ceiling you aren't measuring. Most teams have no idea how many certs they issue per registered domain per week, which means they meet the 50-a-week limit for the first time the week they blow past it. Usually mid-incident. Which is the worst imaginable moment to discover a constraint you've never thought about.
So instrument it. Count issuances per registered domain per rolling week. Count duplicate issuances per name set. Alert when either climbs toward its threshold, not at 100% but at something like 70%, so you've got room to react before the CA starts saying no. Track failed validations per hostname per hour as an early warning for a retry storm already underway. Issuance volume deserves to be a first-class operational metric, right next to error rate and latency on the dashboard, because at 47-day lifetimes it can take down production just as cleanly as either of those.
The annoying part is that this data lives in five different places: ACME account logs, CA dashboards, whatever your renewal tooling deigns to emit. Stitching it into one view across multiple CAs is precisely the kind of glue work that never gets prioritized until the day it's on fire. Watching renewal and issuance activity across a multi-CA estate in one place, so you can see a per-domain bucket filling up before the CA tells you it's full, is a big part of what CertPulse is for. Use whatever gets you there. The point holds either way: learn your issuance rate before the wall, not after you've hit it.
The renewal ceiling was theoretical at 90 days. At 47 it's just arithmetic, and the arithmetic doesn't care whether you ran the numbers ahead of time or found out the hard way.
This is why we built CertPulse
CertPulse connects to your AWS, Azure, and GCP accounts, enumerates every certificate, monitors your external endpoints, and watches Certificate Transparency logs. One dashboard for every cert. Alerts when auto-renewal fails. Alerts when certs approach expiry. Alerts when someone issues a cert for your domain that you didn't request.
If you're looking for complete certificate visibility without maintaining scripts, we can get you there in about 5 minutes.