Part VI: Running PKI · Chapter 20

ACME and the 47-day schedule

In one sentence

ACME lets software prove control of a name, then fetch, renew and install certificates automatically: the only practical way to live with very short certificate lifetimes.

Who is this for

Level: Practitioner

On this page
  1. The idea
  2. How it really works
  3. At work
  4. Common mistakes
  5. Why it matters to the business
  6. Check yourself
  7. Go deeper

The idea

  • Beginner

Getting a certificate by hand takes many steps. You create a key and a CSR, paste the CSR into a CA’s web portal, prove that you control the domain, download the certificate, install it with its chain, reload the server and set a reminder for next time. Done once a year, that is tolerable. Done at least eight times a year for every name, across hundreds of names, it is not.

ACME, the Automatic Certificate Management Environment, is an open protocol that lets software do all of it. A program on your side, the ACME client, talks to the CA’s ACME server. The client:

  1. creates an account with the CA;
  2. orders a certificate for one or more names;
  3. proves it controls those names by answering a challenge, for example by publishing a token at a web address or in DNS;
  4. sends a CSR and downloads the signed certificate;
  5. installs it, reloads the service and starts again before the certificate expires.

The private key never leaves your server. ACME was built for Let’s Encrypt and is now an internet standard, RFC 8555. Many public CAs support it, and so do private CA products, so the same tools work for internal certificates too.

ACME does not remove the need for care. A renewal robot that fails quietly is worse than a person with a calendar, because everyone assumes it works. Automation and monitoring go together (see Inventory, expiry and renewal).

How it really works

  • Developer
  • Expert

What happens in an ACME exchange?

Every ACME request is an HTTPS POST whose body is signed with the client’s account key, a key pair that belongs to the ACME account and is separate from the certificate’s key. Each request also carries a fresh one-time value, a nonce, so that a recorded request cannot be replayed (RFC 8555 section 7.2). The client finds every endpoint in the server’s directory (section 7.1.1). This is Let’s Encrypt’s real directory:

curl -s https://acme-v02.api.letsencrypt.org/directory
{
  "Gzv7tmjYRzs": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
  "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "profiles": {
      "classic": "https://letsencrypt.org/docs/profiles#classic",
      "shortlived": "https://letsencrypt.org/docs/profiles#shortlived",
      "tlsserver": "https://letsencrypt.org/docs/profiles#tlsserver"
    },
    "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.8-July-06-2026.pdf",
    "website": "https://letsencrypt.org"
  },
  "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
  "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
  "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
  "renewalInfo": "https://acme-v02.api.letsencrypt.org/acme/renewal-info",
  "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert"
}

The odd first entry is deliberate: Let’s Encrypt adds a random key so that clients learn to ignore fields they do not know. Then the exchange runs like this:

  1. Account (section 7.3). The client registers its account key with a POST to newAccount. Commercial CAs often link this to an existing customer account with External Account Binding (section 7.3.4).
  2. Order (section 7.4). The client lists the names it wants. An order moves through the states pending, ready, processing and valid, or ends as invalid (section 7.1.6).
  3. Authorization (section 7.5). For each name, the server offers one or more challenges.
  4. Challenge (section 7.5.1). The client publishes a key authorization, the challenge token joined to a hash of its account key (section 8.1), and tells the server it is ready. The CA checks it from the internet.
  5. Finalize. Once every authorization is valid and the order is ready, the client sends its CSR to the order’s finalize URL.
  6. Download (section 7.4.2). The client fetches the certificate with its chain in PEM format.
The ACME flow between a client and a CA: account, order, challenge, validation, finalize and download ACME client on your server ACME server at the CA 1. create account (account key) 2. new order for the names 3. authorizations and challenges 4. challenge ready: please check 5. CA checks the file, the TXT record or TLS-ALPN 6. finalize: send the CSR 7. download the certificate chain ✓ Renewal: ask renewalInfo (ARI) when to renew, then repeat from step 2
One ACME issuance, from account to certificate.

How does the CA check that you control the name?

There are three standard challenge types:

Challenge What the CA checks Port Wildcards Good fit
HTTP-01 (RFC 8555 section 8.3) A file at http://<name>/.well-known/acme-challenge/<token> 80 No One web server, or a load balancer that serves the file
DNS-01 (section 8.4) A TXT record at _acme-challenge.<name> holding the base64url SHA-256 digest of the key authorization none Yes Wildcards, many servers, names that are not reachable from the internet
TLS-ALPN-01 (RFC 8737) A special certificate offered on a TLS connection that negotiates the protocol acme-tls/1 443 No When port 80 is closed and the client controls the TLS endpoint

Let’s Encrypt’s FAQ is explicit: “Wildcard issuance must use the DNS-01 challenge.” Its challenge-types page lists the trade-offs, and warns that “Keeping API credentials on your web server is risky”. You can limit that risk: the same page says “you can use CNAME records or NS records to delegate answering the challenge to other DNS zones”. Point _acme-challenge at a small zone that holds nothing else, and give the client an API key for that zone only.

Publicly trusted CAs must also check domain control from several network locations, not just one (Baseline Requirements section 3.2.2.9). This makes it much harder to fool the CA by hijacking the network path to a single location.

When should a client renew?

Old clients renewed on a fixed schedule, such as a monthly cron job, or a fixed number of days before expiry. ACME Renewal Information, ARI, replaces that guesswork. It is RFC 9773, on the IETF standards track. The CA publishes a renewalInfo endpoint, and for each certificate it suggests a renewal window. RFC 9773 explains why: a CA “could suggest that clients renew prior to a mass-revocation event to mitigate the impact of the revocation”, or spread out a coming spike of renewals. The recommended client behaviour is to “select a uniform random time within the suggested window”.

Anyone can ask. The certificate is identified by its Authority Key Identifier and serial number, both base64url-encoded and joined with a dot. This is the real answer for the letsencrypt.org certificate seen in this handbook:

curl -s https://acme-v02.api.letsencrypt.org/acme/renewal-info/uVnyjs8i8IbTN0j_dhQYuoLYVYc.BUOTO-OGs6KzrdU08hA7yLZf
{
  "suggestedWindow": {
    "start": "2026-11-02T17:18:36Z",
    "end": "2026-11-04T12:29:25Z"
  }
}

The certificate runs from 4 September to 3 December, so the window falls about two-thirds of the way through its life. Let’s Encrypt’s integration guide asks clients to check ARI “at least twice a day”. Without ARI, it recommends renewing “when they have a third of their total lifetime left”, or halfway through for very short-lived certificates.

A real renewal window: a 90-day certificate whose ACME Renewal Information window falls about two-thirds through its life The client checks renewalInfo twice a day and renews inside the window in service time to retry and fix problems 4 Sep: issued ✓ 2–4 Nov ARI window 3 Dec: expires Real example: letsencrypt.org, October 2026. The CA may move the window.
Renewing at about two-thirds of the lifetime leaves a third for retries.

What does the shrinking schedule change for ACME?

Two limits shrink together. Certificates will last at most 47 days from 15 March 2029, and a CA may reuse a domain validation for only 10 days from 15 March 2029. So nearly every renewal will need a fresh challenge. Your challenge path, whether the web root, the DNS API or port 443, must work every single time, not just on the day you set it up.

Let’s Encrypt has published its own path (announcement). Today its default is 90 days. It offers 45-day certificates in its opt-in tlsserver profile from 13 May 2026. The default falls to 64 days from 10 February 2027, then to 45 days from 16 February 2028, when its authorization reuse also drops to 7 hours. It also offers six-day certificates to those who opt in. The real directory above shows the three profiles, classic, tlsserver and shortlived, and a client picks one when it orders.

What are rate limits?

Public CAs limit how often one account or domain may request certificates, to protect the service. Let’s Encrypt’s current limits (rate limits) include up to 50 certificates per registered domain every 7 days, up to 5 certificates for the exact same set of names every 7 days and up to 5 failed validations per name, per account, per hour. A healthy client renewing on time never comes close. A broken script that retries in a tight loop hits them fast. Two habits avoid trouble:

  • Test against staging. Let’s Encrypt recommends its staging environment “when testing or troubleshooting your applications”.
  • Use ARI. “Renewals coordinated by ARI offer the unique benefit of being exempt from all rate limits.”

Can I run ACME for internal names?

Yes. A private CA can speak ACME too. For example, smallstep’s step-ca “works with any ACME-compliant (specifically, ACMEv2; RFC8555) client”, and by default it issues certificates with “much shorter 24 hour lifetimes” (smallstep). You point the same clients at your own directory URL. The CA can still validate names your public DNS never sees, as long as it can reach them. The private CA chapter covers when that is a good idea.

At work

  • Developer
  • IT ops

Which client should I use?

Client Where it fits ARI support
certbot Linux servers, with nginx and Apache plugins Yes, since certbot 4.1.0
acme.sh Any Unix shell, no dependencies, many DNS APIs. Its default CA is ZeroSSL, so set the CA you want explicitly Yes, on by default since acme.sh 3.1.4
lego A single Go binary, and a Go library Yes (RFC 9773 listed)
cert-manager Kubernetes: certificates as resources, stored in secrets Experimental: off by default, behind the ACMEUseARI feature gate (alpha since v1.21)
win-acme Windows and IIS; it creates a scheduled task for renewal Yes, based on a draft of the ARI specification

How do I test certbot safely?

sudo certbot renew --dry-run

--dry-run performs “a test run against the Let’s Encrypt staging server, obtaining test (invalid) certificates but not saving them to disk”. Run it after every change to DNS, firewalls or web server configuration. Most packages install a timer or cron job that runs certbot renew regularly. Check that it exists with systemctl list-timers.

Then make sure the service picks up the new certificate. A deploy hook runs “once for each successfully issued certificate, including on subsequent renewals”:

sudo certbot renew --deploy-hook "systemctl reload nginx"

Point your server at fullchain.pem, not cert.pem. The certbot documentation says fullchain.pem “is what Apache >= 2.4.8 needs for SSLCertificateFile, and what Nginx needs for ssl_certificate”. Serving cert.pem alone leaves out the intermediate. Browsers often hide that mistake, but curl, Java and Python fail with errors like unable to get local issuer certificate or verify error 21.

How do I do it in Kubernetes?

With cert-manager, you declare an issuer once and a Certificate per name. This example uses Let’s Encrypt’s staging directory and the HTTP-01 challenge through an nginx ingress:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: pki-team@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: www-example-com
  namespace: my-app
spec:
  secretName: www-example-com-tls
  dnsNames:
    - www.example.com
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer

cert-manager stores the key and certificate in the secret www-example-com-tls and renews by default “2/3 through the X.509 certificate’s duration” (cert-manager). Switch the server to the production directory once staging works. Check progress with kubectl get certificate -n my-app and kubectl describe certificate www-example-com -n my-app.

How do I point a client at a private ACME server?

Most clients take a directory URL. With certbot, add --server:

sudo certbot certonly --webroot -w /var/www/html \
  --server https://ca.internal/acme/acme/directory -d app.internal.example

The client host must trust the private CA’s root to talk to that server over HTTPS.

The lifetime calendar shows how often your certificates renew under each step of the schedule:

Try it: Lifetime calendarOpen the full tool

Runs in your browser. Nothing you type or paste is sent anywhere.

Start from

The schedule for public TLS certificates

Issued fromLongest lifetimeDomain check reuseRenewals a year, at least
1 September 2020398 days398 days (from 1 October 2021)1
15 March 2026200 days200 days2
15 March 2027100 days100 days4
15 March 202947 days10 days8

From the CA/Browser Forum Baseline Requirements (sections 6.3.2 and 4.2.1), checked 20 October 2026. Renewals a year: 365 divided by the lifetime, rounded up; renewing earlier means more.

Common mistakes

  • “ACME is a Let’s Encrypt thing.” It is an internet standard. Many public CAs and private CA products speak it, so one set of tools covers all your certificates.
  • “HTTP-01 can get me a wildcard.” Wildcards need DNS-01.
  • “The renewal job runs, so we are covered.” Only if the service reloads the new certificate and your monitoring checks the live endpoint.
  • “I’ll test against production.” Use staging or --dry-run. A broken script that loops against production quickly hits the rate limits.
  • “Renew on the first of every month.” Fixed schedules cluster load and ignore the CA’s advice. Check ARI often and renew inside the suggested window.
  • “Give every web server full DNS API access.” Delegate _acme-challenge to a small zone with a narrowly scoped key.

Why it matters to the business

  • Leader

Check yourself

Check yourself

You need a certificate for *.example.com. Which ACME challenge can you use?

  1. HTTP-01
  2. DNS-01
  3. TLS-ALPN-01
Show answer

Answer B DNS-01

Check yourself

What does ACME Renewal Information (ARI) give an ACME client?

  1. A window of time in which the CA suggests renewing
  2. A copy of the CA's private key for faster signing
  3. A list of revoked certificates
Show answer

Answer A A window of time in which the CA suggests renewing

Check yourself

You changed your firewall and want to know whether certbot can still renew. What is the safe check?

  1. Delete the certificate and request a new one
  2. Run certbot renew --dry-run
  3. Wait for the expiry email
Show answer

Answer B Run certbot renew --dry-run

Go deeper

In the gameLevel 20: Robots that renewRobot Workshop, Keystone. In development

The crash course covers ACME in lesson 9, Running PKI.

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9