Domnr
SSL guide

ACME + ARI: automation that survives 47-day certificates

Surviving short-lived certificates means renewal automation that never blinks. A deep dive into ACME's challenge types and the ARI extension (RFC 9773) — how CAs now tell your client exactly when to renew, how to avoid thundering herds, and how mass revocation stops being a fire drill.

Kevin Langley Jr Published 11 min read

Part 2 of 5 in TLS in 2026. Last week, the 47-day certificate era made the case that manual renewal is finished. This week: what robust automation actually looks like when the certificate authority needs a say in your renewal timing.

Here is the renewal logic running in a depressing number of production systems right now:

if days_until_expiry(cert) < 30:
    renew(cert)

It has worked for years. It is also a time bomb, and the 47-day schedule is the fuse. Two assumptions baked into that one line are about to break: that you get to decide when to renew, and that renewing on a fixed offset is harmless. Neither survives contact with short-lived certificates at fleet scale. Fixing it is what ACME Renewal Information (ARI) is for — but to see why, we need to be precise about what ACME does and doesn’t give you.

ACME, quickly

ACME (RFC 8555) is the protocol behind Let’s Encrypt and essentially every modern certificate workflow. The mechanics, compressed:

  1. Account — your client registers a key pair with the CA. Everything you do is signed with that account key.
  2. Order — you ask for a cert covering one or more identifiers (domains).
  3. Authorization + challenge — for each identifier, the CA hands you a challenge to prove you control it.
  4. Finalize — you submit a CSR; the CA issues.
  5. Download — you fetch the certificate and chain.

As a picture, the issuance path is a short pipeline where any stage can stall:

  register ──▶ newOrder ──▶ authz ──▶ challenge ──▶ finalize ──▶ download
  (account)    (domains)   (per      (prove        (submit      (cert +
                           domain)   control)      CSR)         chain)
                              │           │
                              └─ this is where automation lives or dies ─┘

The interesting part for operators is the challenge stage, because the challenge type you can use dictates how automatable a given certificate actually is:

  • http-01 — serve a token at http://<domain>/.well-known/acme-challenge/…. Simplest to reason about, but it needs port 80 reachable for that path, which breaks behind some CDNs, WAFs, and split-horizon setups.
  • dns-01 — publish a TXT record at _acme-challenge.<domain>. The only challenge that can issue wildcards, and the one that works for hosts not exposed to the public internet — but it makes your DNS provider’s API a hard dependency of certificate issuance, and drags in DNS propagation timing. (If you’ve read our pieces on DNS propagation and DNS change monitoring, you already know that “the record is published” and “the record is visible everywhere” are not the same moment.)
  • tls-alpn-01 — prove control over a TLS connection on port 443 using a special ALPN protocol. Good for locked-down environments where only 443 is open; needs a terminator that can speak it.

ACME solved issuance. What it historically left underspecified is when to renew — and at 47 days, that’s the whole game.

Why fixed-offset renewal breaks

The < 30 days cron fails in two distinct ways, and the second is the one that keeps CAs up at night.

Failure one: you miss an early revocation. A certificate’s expiry is the latest it could need replacing, not the only reason. If the CA discovers a mis-issuance, a compliance problem, or a key-handling bug, it may need that cert replaced now — days or weeks before its expiry date. A renewal policy keyed purely off the expiry date is structurally blind to this. You’d find out when the cert got revoked out from under you, not before.

Failure two: synchronized renewal storms. If thousands of your certs — or millions across a CA’s whole customer base — all renew at “30 days before expiry,” renewals cluster. Issue a big batch of certs on the same afternoon and they’ll all come due for renewal on the same future afternoon. That thundering herd hammers the CA’s issuance infrastructure in spikes, which is exactly when rate limits and outages happen. Fixed offsets are correlated by construction.

What’s needed is for the CA — the one entity that knows about early revocations and sees the aggregate load — to have input into your renewal timing. That’s ARI.

ARI: letting the CA schedule you

ACME Renewal Information was published as RFC 9773 in 2025. The idea is small and elegant: the CA exposes a renewalInfo endpoint, and for any certificate you hold, you can ask “when do you suggest I renew this?” The CA answers with a window, not a deadline.

You look a certificate up by an identifier derived from its Authority Key Identifier and serial number (so the CA knows exactly which cert you mean without you sending the whole thing). A request and response look like this:

GET /acme/renewal-info/aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE HTTP/1.1
Host: acme.example-ca.org

HTTP/1.1 200 OK
Content-Type: application/json
Retry-After: 21600

{
  "suggestedWindow": {
    "start": "2026-08-12T00:00:00Z",
    "end":   "2026-08-15T00:00:00Z"
  },
  "explanationURL": "https://acme.example-ca.org/docs/why-this-window"
}

Three fields carry all the weight:

  • suggestedWindow — a start and end (RFC 3339 timestamps) bounding when the CA would like you to renew. Normally this sits comfortably before expiry; the CA can widen or shift it to spread load.
  • explanationURL — an optional human-readable link explaining why the window is what it is. In normal times it points at boilerplate. During an incident, it’s how the CA tells you “this window moved because of a mass-revocation event — here are the details.”
  • Retry-After — how long before you should check again. (Let’s Encrypt currently returns 21600 seconds — six hours.) This matters because the window is not static; you re-poll so you notice if it moves.

The renewal algorithm

The client logic that replaces < 30 days is only a little more involved, and every line of it earns its place:

schedule = none

loop:
    info   = GET renewalInfo(cert)          # keyed by AKI + serial
    window = info.suggestedWindow

    if now >= window.end:
        # we're at or past the window — don't gamble, renew now
        renew(cert)
        schedule = none
        continue

    if schedule is none:
        # pick ONE random time in the window, then commit to it
        lower    = max(now, window.start)
        schedule = uniform_random(lower, window.end)

    if now >= schedule:
        renew(cert)
        schedule = none
    else:
        # re-poll on the CA's cadence; the window may move under us
        sleep(min(schedule - now, retry_after))

The single most important line is uniform_random(lower, window.end). Picking a uniformly random instant inside the window is the herd-avoidance mechanism: every client independently smears its renewals across the window, so even millions of certs issued the same day renew on a smooth distribution instead of a spike. You are not renewing at the start of the window, or the end — you’re renewing at a random point inside it, and that randomness, multiplied across the fleet, is what flattens the load.

  1. Poll renewalInfo for the cert (by AKI + serial)
  2. If now is past the window's end, renew immediately — no gambling
  3. Otherwise pick one uniform-random time inside the window and hold it
  4. Re-poll on the Retry-After cadence; the window can move
  5. Renew at the chosen time; the next window shifts forward

A note on keys: rotate them now that it’s cheap

There’s a quiet decision hiding inside “renew”: do you generate a fresh key pair, or re-certify the existing one? ACME allows either — the CSR can carry the same public key every time, or a new one. At one renewal a year, key rotation was a deliberate event many teams skipped to avoid disturbing anything that referenced the key. At a renewal every few weeks, that calculus flips. Rotation becomes routine, nearly free, and a genuine security win: a private key that’s only ever valid for six weeks is a far smaller prize than one that’s been sitting on a load balancer for three years. Short lifetimes give you key hygiene almost for free — take it, and rotate on every renewal by default.

The one thing to check before you make rotation automatic is whether anything pins your key. Browser key pinning (HPKP) is dead and gone, but DANE/TLSA records pinning a specific key still exist in some mail and infrastructure setups, and certificate or key pinning lingers in mobile apps and hard-coded clients. If a downstream consumer pinned your key, rotating it breaks them — silently, and only for the pinned clients, which is the worst kind of breakage to diagnose. The fix isn’t to stop rotating; it’s to know your pins and either pin the CA / a long-lived intermediate instead, or retire the pin. In a world of weekly certificates, pinning a leaf key is simply incompatible with how the web now works.

Mass revocation stops being a fire drill

Here’s the payoff that makes ARI more than a load-balancing trick. Web PKI history has a recurring nightmare: a CA discovers a bug affecting a huge batch of certificates and is contractually required to revoke them on a brutally short clock — often 5 days, sometimes 24 hours. The 2020 Let’s Encrypt CAA-recheck incident put roughly three million certificates in that bucket. For everyone relying on those certs, it meant a panicked, unplanned, all-hands renewal scramble triggered by an email.

ARI dissolves that scenario. A CA that needs a batch renewed early simply moves those certificates’ suggested windows into the (near) past and sets explanationURL to the incident notice. Every ARI-aware client, on its next poll, sees a window that’s already started — or already ended — and renews gracefully on its own. No email blast, no war room, no thundering herd of panicked renewals. The same polling loop that handles routine renewal handles the emergency, because to the client they look identical.

Fixed-offset cron (< 30 days)ARI-driven renewal
You decide timing, blind to the CA CA suggests a window; you choose within it
Synchronized renewals → load spikes Uniform-random in-window → smooth load
Early revocation = surprise outage Early revocation = window shifts, client self-heals
Mass revocation = all-hands fire drill Mass revocation = a normal poll cycle
Renewal cadence is a guess Renewal cadence is negotiated, per cert

Putting it into practice

You mostly don’t implement this yourself — you adopt a client that already speaks ARI and make sure it’s actually turned on. Support has landed across the common clients (Certbot, the acmez/Caddy stack, lego, and others); the trap is running a version or config where ARI is dormant and you’re still effectively on a fixed offset. A few things worth doing regardless of client:

  • Confirm your ACME client polls renewalInfo and honors the suggested window — not just a static threshold
  • Verify it picks a random time inside the window, not the window's edge
  • Ensure renewals overlap: new cert deployed and serving before the old one is retired
  • Instrument the renewal pipeline itself — success/failure counts, time-to-deploy, window drift
  • Make dns-01 validation as reliable as issuance: propagation waits, provider API retries, rate-limit handling
  • Alert on the gap between 'renewed at the CA' and 'renewed on the endpoint'

That last point is where the most automation matures: you are now operating a fleet of renewals, which is itself a distributed system that can fail partially and silently. The renewal succeeding at the CA is necessary but not sufficient — the certificate still has to land on every endpoint that serves it.

Next week

ACME plus ARI is how you survive the frequency of short-lived certificates — the changing cadence of TLS. But 2026 is also changing what’s inside the handshake. Next: post-quantum key exchange, why a third of real HTTPS traffic is already using it, and what it takes to migrate without a flag day.