Tutorial

Your load balancer has a certificate ceiling, and renewal churn will find it

August 9, 20269 min readCertPulse Engineering

The first time it happens, the error is confusingly boring. You're mid-deploy, Terraform is applying a listener certificate attachment for a new tenant domain, and the API returns a quota error naming a limit you'd never heard of. Everything else applied fine. The cert exists in ACM, valid, unused. The listener just won't take it.

Load balancers have certificate ceilings. They always have. What's changing is that shorter certificate lifetimes turn a static inventory problem into a traffic problem: every rotation is an API call against a resource that has both a count limit and a rate limit, and the number of those calls goes up roughly tenfold between now and 2029.

Here's where the ceilings sit, and how to figure out whether yours is close.

Where the ceilings actually are

AWS: 25 per load balancer, and the name matters

People search for the ALB certificate limit per listener. AWS names the quota per load balancer: "Certificates per Application Load Balancer," default 25, excluding the default certificate. Network Load Balancers get the same 25 for TLS listeners.

That distinction isn't pedantic. If you're running one HTTPS listener on port 443, per-listener and per-load-balancer are the same number and nobody notices. If you added a second listener on 8443 expecting a fresh budget of 25, you didn't get one. Enforcement happens at the AddListenerCertificates call, which is why the error looks listener-shaped, but the accounting is at the load balancer level. Check Service Quotas for your own account rather than trusting a blog post, this one included. AWS adjusts defaults and doesn't announce it loudly.

The 25 is adjustable. Support will raise it, in my experience without much argument, but it's a ticket with a turnaround, not a switch you flip mid-incident.

Sitting above that is the regional ACM quota: a few thousand ACM-issued certificates per region by default. Imported certificates count against a separate, lower quota, and that's the one that catches teams running an internal CA or a commercial CA and pushing certs into ACM. If your issuance path is "external CA, import to ACM, attach to listener," you have two ceilings stacked and you probably only know about one.

GCP: 15, and it's not adjustable in the way you want

A target HTTPS proxy accepts up to 15 SSL certificate resources. That's the classic path, and 15 is low enough that any multi-tenant setup hits it early.

The escape hatch is Certificate Manager. Instead of attaching certificate resources directly, you attach a certificate map to the target proxy, and the map holds entries that resolve by hostname. The ceiling moves from 15 to something in the thousands. Check the current Certificate Manager quotas page for the exact figure, because that one actually moves.

The wrinkle that matters operationally: Compute Engine SSL certificate resources are immutable. No in-place update. Renewal means creating a new resource, updating the target proxy's certificate list to include it, then deleting the old one. That's a read-modify-write against a shared object, and during the overlap you're holding both. With 15 slots, a fleet-wide simultaneous rotation caps you at seven domains. Certificate Manager avoids most of this, which is reason enough to migrate on its own.

Azure: 100 per Application Gateway, marked fixed

Application Gateway v2 allows 100 SSL certificates per gateway, and the Azure limits table lists it as fixed, not adjustable. It also allows 200 HTTP listeners, so you'll run out of certificates before you run out of listeners in any multi-site configuration.

Application Gateway pulls certificates from Key Vault via managed identity and polls for new versions every four hours. Key Vault itself doesn't cap certificate count in a way you'll hit, but it throttles transactions per vault per region, and certificate creation has a tighter budget than reads. The failure mode is quiet. If the poll fails — expired managed identity permission, soft-deleted secret, throttling during a batch rotation — the gateway keeps serving the previous certificate and says nothing. It'll happily serve an expired one.

CloudFront: one certificate, and the SAN list becomes the ceiling

CloudFront takes exactly one ACM certificate per distribution, from us-east-1, and that certificate must cover every alternate domain name on the distribution. The alternate domain name quota is 100 by default and adjustable.

So the ceiling isn't "certificates per distribution," it's "names per certificate." ACM's default names-per-certificate quota is in the low tens and adjustable upward. Let's Encrypt caps at 100 identifiers per certificate, hard. Either way, every hostname addition is a certificate reissue plus a distribution update, and distribution updates propagate on CloudFront's schedule, not yours.

Azure Front Door has its own custom-domain ceilings that don't match Application Gateway's. Run both and you're tracking two unrelated numbers.

SNI made this easy to ignore

None of this was a problem when adding a hostname meant provisioning an IP. SNI made it one API call. Add tenant, attach cert, done. No capacity planning, no review, no visible cost.

So the count creeps. Every new customer with a vanity domain, every marketing microsite, every regional variant of a hostname, every acquired product parked behind the shared ingress because that was faster than standing up its own. Nobody tracks the total because nothing in the workflow surfaces it. The count is a number you have to go ask for, and there's no reason to ask until the answer is bad.

The first signal is usually a failed attach in a deploy pipeline, for a customer who's waiting on it.

The renewal-churn multiplier

Here's the part that changes with SC-081v3.

Under 398-day certificates, each one rotates roughly once a year. Under the 200-day maximum in force since March 2026, with a client renewing at two-thirds of lifetime, about three times a year. At 100 days from March 2027, about six. At 47 days from March 2029, about twelve.

Same fleet, ten times the rotation events. If those rotations touch your load balancer configuration, you now have ten times the API calls hitting a resource with a rate limit.

Worth being precise about which rotations actually churn the listener. ACM-issued certificates attached to an ALB renew in place. The ARN doesn't change, ACM swaps the material, the listener never gets modified. If your entire estate is ACM-managed on AWS, this section is a smaller problem for you.

The churn comes from everything else. GCP's immutable certificate resources, by design. cert-manager configs that mint a new Secret and trigger an ingress reconcile. Terraform with create_before_destroy on listener certificate attachments. Any pipeline that issues externally and imports as a new resource rather than a new version of an existing one. In those paths, every renewal is at minimum an attach, usually an attach plus a detach.

Two things go wrong at volume. First, transient double-holding: during create-before-destroy you hold old and new at once, so your effective capacity is half the quota if rotations align. Second, throttling. ELBv2 and Compute Engine both rate-limit mutating API calls, and a batch rotation that trips the limit doesn't fail cleanly — it partially applies. Some certificates land, some get a throttling error, and the retry logic in your automation may or may not be smart enough to notice. You end up with a subset of hostnames serving the old certificate while your tooling reports success.

The batching isn't hypothetical. Certificates issued together renew together. If you onboarded forty tenants during a migration week two years ago, those forty certificates have been renewing in the same window ever since, and at 47 days that window comes around every month.

Consolidation math and what it costs you

The obvious move when you're near a slot ceiling is to pack more names per certificate.

SAN certificates work. One slot, many hostnames, count problem gone. The costs are real, though. You've concentrated blast radius: a botched renewal on a 60-name certificate takes down 60 hostnames at once, and validation now has to succeed for every name on the certificate or the whole reissue fails. That matters when one of those names belongs to a customer whose DNS delegation broke last Tuesday. You also hit the per-certificate name limits above, so you've traded one ceiling for another, just further out.

Wildcards dodge the slot problem entirely and don't need reissuing when you add a subdomain. They also reintroduce key distribution. The same key material now sits on every host that terminates for that domain, and revoking it is an estate-wide event. And they don't help at all for customer vanity domains, which is usually what drove the count up in the first place.

Self-managed termination with nginx, HAProxy, or Envoy has no hard certificate cap. What it has is reload cost. nginx loads certificates into memory per worker process, and at a few thousand certificates a reload means a measurable pause plus doubled memory while old workers drain. HAProxy handles this better: its runtime API lets you add and commit a certificate without a reload at all, which is the right primitive for high-churn environments and an underrated reason to pick it. nginx needs either a reload or a Lua-based dynamic certificate hook.

Checks to run before you're at the ceiling

Four things, in order of how quickly they pay off.

Get the current count per listener and per region. On AWS that's DescribeListenerCertificates for each HTTPS listener, cross-referenced against the Service Quotas API for the applied value, not the default — a past support ticket may have already raised yours. On GCP, read the sslCertificates list on each target HTTPS proxy, or the map entry count if you're on Certificate Manager. On Azure, the sslCertificates collection on each Application Gateway.

Alert on percentage of quota consumed, not just expiry. Certificate monitoring almost universally means expiry monitoring, and quota consumption is invisible to it. A listener at 23 of 25 is fine today and broken during the next onboarding. Set the threshold low enough that a support ticket has time to land. 70% is a reasonable start for adjustable quotas, lower for the fixed ones.

Measure headroom against peak simultaneous rotations, not steady state. This is the check people skip. If your renewal automation uses create-before-destroy and your certificates cluster into a rotation window, your usable slot count is the quota minus whatever gets double-held at the peak. Twenty steady-state certificates on a 25-slot listener sounds comfortable and isn't, if twelve of them renew in the same six-hour window.

Jitter your renewal schedule. If you find clustering, spread it. Randomizing the renewal offset by a few days per certificate flattens the peak and costs nothing. Do it before you need it, because doing it during a rotation storm means shortening some certificate's life to move it, which is its own small adventure.

The reason this is annoying to check by hand is that the numbers live in three quota systems with three naming conventions, and the certificate count that matters is spread across accounts, subscriptions, and projects that different teams own. A cross-account inventory (CertPulse does this, as do a handful of other tools) turns "which of my 40 listeners is closest to its cap" into a query instead of an afternoon. Whatever you use, the useful property is that it counts certificates per attachment point, not just per certificate authority.

The ceilings aren't changing. The rate at which you bump into them is, and that schedule is already published.

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.