TLS certificate automation in 2026: revisit cert-manager, Certbot and step-ca before the next certificate expires on you

Think of a bicycle and auto parts manufacturer in Ludhiana that sells through about 400 dealers across Punjab, Haryana, Rajasthan and the hill states (an invented example, not a real client). Dealers place orders on a portal that runs on two nginx virtual machines, a small k3s cluster hosts the order API and a parts catalogue behind an ingress controller, and the shop floor in Focal Point has a production tracking server, a label printing gateway and an MQTT broker that the machines talk to. A payment gateway calls back into the portal when a dealer pays. The IT team is two people and one contract developer.

Last March the dealer portal showed a browser warning on a Sunday morning. Certbot had renewed the certificate on Friday night, but nobody had added a hook to reload nginx, so nginx kept serving the old one until it expired. In June the label gateway stopped printing because its self-signed certificate, made ten years ago by a vendor engineer, ran out. And the ERP vendor’s appliance still uses a one-year certificate bought from a commercial CA, which someone renews by hand from a calendar reminder. The finance head now asks a fair question: if certificates are going to get shorter every year, how many more Sundays like this should the company expect?

This post compares cert-manager, Certbot and step-ca as of 20 October 2026, with lego and acme.sh where they change the picture: the CA/Browser Forum schedule that cuts public certificate lifetimes, what Let’s Encrypt changes and when, governance and licences, current versions, challenge types, renewal timing with ACME Renewal Information (ARI) and profiles, deploy and reload, monitoring, advisories from 2025 and 2026, and everyday traps. Earlier Revisit.Tech posts on ingress controllers and secrets managers cover what sits around a certificate; here the focus is the certificate itself.

The facts come from primary material: the CA/Browser Forum ballot page and the current Baseline Requirements, Let’s Encrypt announcements and documentation, GitHub release and advisory data, the projects’ own documentation, and a drill on one shared box that ran Certbot 5.8.0, lego 5.5.2 and acme.sh 3.1.6 against Let’s Encrypt’s test CA Pebble 2.10.1 and against step-ca 0.30.2. cert-manager could not be run on the box, and the drill says so. Where I found no primary source, I write “Not documented”. What I say about licences is my own reading, not legal advice.

The short version

  • The cap is already lower than most runbooks assume. Under ballot SC-081v3, public TLS certificates issued from 15 March 2026 may last at most 200 days, from 15 March 2027 100 days, and from 15 March 2029 47 days. Domain validation reuse falls on the same schedule, to 10 days in 2029.
  • Let’s Encrypt moves earlier than the cap. Its default certificates go from 90 to 64 days on 10 February 2027 and to 45 days on 16 February 2028. A 45-day option is already available through the tlsserver profile, and six-day certificates are generally available through shortlived.
  • Renewal is three jobs, not one. Get the new certificate, put it where the server reads it and reload the server, then prove from outside that the new one is being served. Most expiry incidents happen in the second or third job, as in the Ludhiana story.
  • ARI changes who decides when to renew. With ACME Renewal Information (RFC 9773), the CA suggests a renewal window and can pull it forward after a revocation. Certbot, lego and acme.sh use it by default today. cert-manager 1.21 has it behind an alpha feature gate that is off by default. step-ca 0.30.2 does not advertise it.
  • Pick by where the certificate lives. cert-manager for Kubernetes Secrets; Certbot, lego or acme.sh for virtual machines and appliances; step-ca for private names with your own short-lived internal CA. Most organisations end up with two of these, and that is fine.
  • Hard-coded numbers are the real risk. A cron job that renews “30 days before expiry” breaks on a 45-day certificate. A client that renews at about two thirds of the lifetime, or follows ARI, does not care what the lifetime is.

Where things stand on 20 October 2026

Every time here is IST. Release dates are GitHub published_at values converted from UTC; star counts were taken on 20 October and measure attention, nothing more.

The rules: CA/Browser Forum ballot SC-081v3

  • What it is: “Introduce Schedule of Reducing Validity and Data Reuse Periods”, proposed by Clint Wilson of Apple. Voting closed on 11 April 2025: certificate issuers 25 yes, 0 no, 5 abstain (eMudhra among the yes votes); certificate consumers Apple, Google, Microsoft and Mozilla all yes.
  • Where it lives now: section 6.3.2 of the TLS Baseline Requirements (version 2.1.5, effective 16 May 2025; current version 2.3.1, dated 4 October 2026).
  • Maximum validity: 398 days before 15 March 2026; 200 days from then; 100 days from 15 March 2027; 47 days from 15 March 2029. Each step has a lower “SHOULD NOT exceed” value (199, 99 and 46 days), and CAs are told not to issue for the full maximum by default.
  • Validation reuse: for domain names and IP addresses, 398 days before 15 March 2026, then 200, then 100, and 10 days from 15 March 2029. For other identity information, 825 days falls to 398 days from 15 March 2026.
  • Short-lived certificates: from 15 March 2026, seven days or less, and a CA may skip revocation for them.

Let’s Encrypt

  • Default lifetime plan: announced on 2 December 2025 and restated on 7 October 2026. The opt-in tlsserver profile moved to 45 days on 13 May 2026. On 10 February 2027 the default classic profile moves to 64 days with a 10-day authorisation reuse period, and on 16 February 2028 to 45 days with a seven-hour reuse period. Staging was scheduled to switch to 64 days on 14 October 2026. The last 90-day certificate is expected to expire on 11 May 2027; valid certificates will not be revoked for this.
  • Profiles today: the production directory, read on 20 October, lists classic, tlsserver and shortlived and a renewalInfo endpoint. The profiles page (last updated 4 October 2026) gives classic 90 days, tlsserver 45 days and shortlived 160 hours. IP address certificates must use shortlived. The tlsclient profile is no longer available as of 8 July 2026.
  • Other changes that hit automation: expiry emails ended on 4 June 2025; OCSP was turned off on 6 August 2025, leaving CRLs only; six-day and IP address certificates became generally available on 15 January 2026. Renewals are exempt from rate limits, so shorter lifetimes do not change them.
  • DNS-PERSIST-01: a challenge with a standing TXT record bound to one ACME account, so DNS does not change at every renewal. Ballot SC-088 (adopted 9 October 2025) added the method. Let’s Encrypt targeted production for “some time in Q2 2026”; production status on 20 October 2026: Not documented on the pages I read (the challenge types page was last updated on 12 February 2026).

cert-manager

  • Latest: v1.21.2 (11 September 2026, 1:21 PM), a patch release on the 1.21 line that began with v1.21.0 on 9 July 2026, 4:10 AM. The other supported line is 1.20, latest v1.20.4 (16 September 2026, 10:18 PM). The supported releases page lists 1.22 for about November 2026, and says each release is supported until the second release after it.
  • What 1.21 brought: experimental ARI behind the ACMEUseARI feature gate, renewal windows on the Certificate, AWS IAM authentication for the Vault issuer, and three breaking Helm changes. ACME profile selection exists since 1.18.0 (10 June 2025).
  • Licence and governance: Apache-2.0. Created at Jetstack in 2017; CNCF member since 10 November 2020, graduated on 29 September 2024. No open-source LTS; the releases page lists a commercial 1.17 LTS from Palo Alto Networks until 3 February 2027.
  • Release rhythm and attention: 14 stable releases across supported lines dated 2026; 14,121 stars.

Certbot

  • Latest: 5.8.0 (2 September 2026, 2:34 AM), which needs cryptography 47.0.0 or newer. 5.0.0 arrived on 2 September 2025.
  • Recent changes that matter: 4.0.0 (April 2025) replaced the fixed 30 days before expiry with “less than 1/3rd of its lifetime remains” (half for ten days or less) and added --preferred-profile and --required-profile. 4.1.0 (June 2025) added ARI checks; 5.0.0 stores the ARI Retry-After between runs; 5.3.0 and 5.4.0 (February and March 2026) added IP address certificates.
  • Licence and governance: Apache-2.0 (the nginx plugin includes MIT-licensed code), copyright the Electronic Frontier Foundation and others.
  • Release rhythm and attention: 7 releases dated 2026; 33,279 stars.

step-ca

  • Latest: step-ca v0.30.2 (23 March 2026, 5:47 AM), a dependency update over v0.30.0 (19 March 2026, 4:49 AM), which fixed the SCEP authorisation bypass described below. The step CLI is newer: v0.31.0 (26 September 2026, 1:39 AM). The step-ca changelog has an unreleased section that adds ML-DSA keys and requires Go 1.26.
  • Licence and governance: Apache-2.0, developed by Smallstep, which also sells a hosted CA. Per the policy documentation, the self-hosted server applies name policy only at the authority level; per provisioner and per account policies belong to the hosted product.
  • Release rhythm and attention: 3 stable step-ca releases dated 2026 (7 for the CLI); 8,971 stars for smallstep/certificates.

lego and acme.sh

  • lego: v5.5.2 (23 September 2026, 5:11 PM). v5.0.0 (11 May 2026) renamed flags and added DNS-PERSIST-01. MIT; 23 releases dated 2026; 9,953 stars.
  • acme.sh: 3.1.6 (20 September 2026, 4:45 PM), signed tags and no functional change over 3.1.5. 3.1.4 (17 July 2026) added ARI, DNS-PERSIST-01 and a six-hourly cron job. Default CA ZeroSSL since v3.0 in 2021. GPL-3.0; 4 releases dated 2026; 47,843 stars.

Timeline

  • 2014 to 2018: repositories start for Certbot (2014), lego and acme.sh (2015), cert-manager (2017, at Jetstack) and step-ca (2018).
  • March 2019: ACME is published as RFC 8555. 29 September 2024: cert-manager graduates in the CNCF.
  • 11 April 2025: ballot SC-081v3 passes.
  • 4 June 2025: Let’s Encrypt stops expiry notification emails. 6 August 2025: it turns off OCSP.
  • September 2025: ARI is published as RFC 9773.
  • 9 October 2025: ballot SC-088, the persistent DNS TXT record method, is adopted.
  • 2 December 2025: Let’s Encrypt announces the move to 45 days. 3 December 2025: step-ca v0.29.0 fixes an ACME and SCEP authorisation bypass (CVE-2025-44005).
  • 15 January 2026: six-day and IP address certificates become generally available at Let’s Encrypt.
  • 15 March 2026: the 200-day cap begins.
  • 19 March and 16 April 2026: step-ca v0.30.0 and lego 4.34.0 ship security fixes (CVE-2026-30836, CVE-2026-40611).
  • 13 May 2026: the Let’s Encrypt tlsserver profile issues 45-day certificates.
  • 26 June 2026: cert-manager advisory on direct Challenge resources (CVE-2026-62290).
  • 9 July 2026: cert-manager 1.21.0 with ARI behind a feature gate.
  • 14 October 2026: Let’s Encrypt staging scheduled to issue 64-day certificates.
  • 10 February 2027: Let’s Encrypt default moves to 64 days. 15 March 2027: the 100-day cap begins.
  • 16 February 2028: Let’s Encrypt default moves to 45 days. 15 March 2029: the 47-day cap and 10-day validation reuse begin.

First question: who notices when renewal fails?

Most teams describe certificate renewal as a single step: “Certbot renews it”. In practice it is three separate jobs, and each can fail on its own.

  1. Get. An ACME client proves control of the name, sends a CSR and receives a new certificate. This needs a working challenge path (port 80, DNS API access or TLS-ALPN on port 443) and a working account.
  2. Put. The new certificate and key go to the place the server reads, with the right owner and permissions, and the server reloads. On a VM that is a deploy hook; in Kubernetes it is a Secret that the ingress controller watches; on an appliance it may be an API call or a person.
  3. Prove. Something outside the server checks which certificate is actually being served, and raises an alarm while there is still time. Let’s Encrypt no longer sends expiry emails, so this job is now entirely yours.

With a one-year certificate, a failure in any job had months to be noticed. With 45 days and renewal at two thirds, the margin is about 15 days; with six-day certificates, about three. Shorter lifetimes create no new failure modes. They remove the slack that hid the old ones.

So the honest promise of certificate automation is not “we use Let’s Encrypt”:

“Every certificate we serve was issued by a machine, put in place and reloaded by a machine, and checked from outside by a machine, and a person hears about it when any of those three steps fails, long before the expiry date.”

If one of the three steps still depends on a person remembering, that step is where the next Sunday incident comes from.

What each one actually is

cert-manager 1.21.2

cert-manager is a set of Kubernetes controllers. You declare Issuer or ClusterIssuer resources (ACME, a CA key pair in a Secret, Vault, self-signed, CyberArk, or external issuers) and Certificate resources that name the Secret to fill. The controller creates CertificateRequest, Order and Challenge objects, solves HTTP-01 through a temporary solver pod and route, or DNS-01 through a DNS provider, and writes key and certificate into the Secret that ingress controllers, Gateways and meshes read. Annotations on Ingress and Gateway resources can create Certificates for you.

By default a Certificate lasts 90 days and renews at two thirds of its actual lifetime. The documentation recommends renewBeforePercentage because it adapts when the issuer returns a shorter certificate than asked for. 1.21 added cron-style renewal windows, so renewals can stay inside approved hours.

Certbot 5.8.0

Certbot is the ACME client from the EFF, written in Python, aimed at a single host. It can install certificates into nginx and Apache through plugins, or just fetch them (certonly) with standalone, webroot, manual or DNS plugins. It keeps state in /etc/letsencrypt: the account, an archive of every certificate and key, live/ symlinks and one renewal file per certificate that remembers how it was obtained. Most installation methods set up a timer for certbot renew (the user guide’s manual cron example runs it twice a day); renewal hooks (--deploy-hook or scripts in renewal-hooks/deploy/) are where the reload belongs.

step-ca 0.30.2 with the step CLI 0.31.0

step-ca is a certificate authority you run yourself. step ca init creates a root and an intermediate, a configuration file and a database (Badger by default; BoltDB, MySQL and PostgreSQL are documented). Provisioners decide who may ask for what: an ACME provisioner makes it a private ACME server for Certbot, lego, acme.sh or cert-manager, while JWK, OIDC and cloud provisioners hand out certificates against tokens. Claims set default and maximum lifetimes, templates shape certificate contents, and an authority-level policy restricts which names can ever be issued. step ca renew --daemon renews at about two thirds of the lifetime, with jitter. The documentation recommends keeping the root key offline and using short-lived certificates, which revoke “passively” by simply not being renewed.

lego 5.5.2 and acme.sh 3.1.6

lego is a single Go binary and a Go library; its documentation claims more than 200 DNS providers, plus built-in HTTP-01 and TLS-ALPN-01 servers, webroot mode and hooks. acme.sh is a POSIX shell script with its own long list of DNS APIs and deploy hooks, including routers, NAS boxes and firewalls. Both now follow ARI by default, and both support profile selection and DNS-PERSIST-01.

Side by side

Gaps in the official material are marked “Not documented”, and anything called a drill result comes from the single shared VM described further down.

Licence and governance

  • cert-manager: Apache-2.0, a CNCF graduated project with maintainers from several companies. The CNCF’s 2024 note described Jetstack as part of Venafi, a CyberArk company; the Venafi issuer was renamed to CyberArk in 1.20.
  • Certbot: Apache-2.0, run by the EFF, a non-profit.
  • step-ca: Apache-2.0, run by Smallstep, a company that also sells a hosted CA with features the open-source server lacks, such as per-provisioner policies.
  • lego: MIT, in the go-acme organisation. acme.sh: GPL-3.0, which matters if you modify and ship it inside a product.
  • In practice: all are free to run. The questions are who funds the work and how long a release line lives. cert-manager supports only two minor lines at a time. My reading, not legal advice.

Where it runs and what it manages

  • cert-manager: Kubernetes and OpenShift only (1.21 supports Kubernetes 1.33 to 1.36). Output is a Secret.
  • Certbot: one host at a time; files on disk and, optionally, nginx or Apache configuration.
  • step-ca: a server that issues; clients do the fetching. Private trust only.
  • lego and acme.sh: one host at a time, like Certbot, with many more DNS providers and deploy targets.

Which CAs each one can talk to

  • cert-manager: any ACME CA, including external account binding for commercial ones, plus non-ACME issuers (CA, Vault, Venafi or CyberArk, and external issuer plugins). It is the only one here that can mix public and private issuers in one place.
  • Certbot: any RFC 8555 ACME server through --server, with external account binding. Defaults to Let’s Encrypt.
  • step-ca: is the CA; its documentation also describes adding ACME in front of a legacy subordinate CA.
  • lego: any ACME server, with 16 CA short codes in its help text. acme.sh: any ACME server; ZeroSSL unless you set another.

Challenge types and the DNS credential problem

  • HTTP-01: all five. cert-manager needs the solver reachable through your Ingress or Gateway; a VM needs port 80 or a served webroot.
  • DNS-01: all five, and the only route to public wildcards. The weak point is the DNS API token, which often can change the whole zone and now gets used more often; cert-manager’s 2026 advisory was about exactly such credentials.
  • TLS-ALPN-01: cert-manager solvers are http01 and dns01 only; Certbot removed its TLS-ALPN classes in 5.0.0; lego and acme.sh support it; step-ca validates it.
  • DNS-PERSIST-01: lego 5.x and acme.sh 3.1.4 and later support it. Certbot and cert-manager: Not documented in their release notes up to 20 October 2026. step-ca: Not documented.
  • Device attestation: step-ca supports device-attest-01 for TPM and Apple devices.

Renewal timing: thresholds, ARI and profiles

  • cert-manager: two thirds of the lifetime by default, or renewBefore and renewBeforePercentage. ARI is alpha and off by default (ACMEUseARI), falling back to the normal logic if the CA has no renewalInfo. Profiles through the profile field on an ACME issuer since 1.18.
  • Certbot: less than one third of the lifetime left, or half for ten days or less; ARI checked by default on every renew, with Retry-After stored between runs. --preferred-profile falls back to the CA default if the profile disappears; --required-profile fails instead.
  • step-ca: as a server, it does not advertise renewalInfo or profiles (drill). Lifetimes come from claims on the provisioner. Clients use their own thresholds.
  • lego: one third left, or half for short-lived; ARI on by default; --profile.
  • acme.sh: follows ARI by default since 3.1.4. Without ARI it renewed a 72-hour step-ca certificate one day before expiry in the drill, and the script still carries a DEFAULT_RENEW of 30 days for its older schedule. --cert-profile selects a profile.
  • Drill: after a revocation the CA’s ARI window moved into the past. lego renewed on its next run; Certbot waited for its stored six-hour Retry-After, then renewed.

Deploy and reload

  • cert-manager: writes the Secret; reload depends on the consumer. Ingress controllers usually watch Secrets; an application that mounts the Secret as files must re-read them itself.
  • Certbot: the nginx and Apache installers reload for you. With certonly, you need a deploy hook. Since 5.3.0, deploy hook directories also run on certonly and run.
  • step-ca clients: step ca renew --daemon can run a command after each renewal (--exec).
  • lego: --deploy-hook, --pre-hook and --post-hook. acme.sh: --install-cert with a --reloadcmd, plus deploy hooks for many products.
  • Common gap: appliances that take a certificate only through a web form. No tool here fixes that; the vendor must offer ACME or an API.

Where keys and state live

  • cert-manager: private keys in Kubernetes Secrets, so anyone who can read Secrets in that namespace can read keys. The default private key rotation policy is Always (GA), so every renewal makes a new key.
  • Certbot: keys under /etc/letsencrypt/archive, readable by root, with every old key kept. Back up and protect that folder as you would a password store.
  • step-ca: the intermediate key on the CA host (or in a KMS or HSM), the root ideally offline, and a database of issued certificates and ACME state.
  • lego and acme.sh: files under their own folders; acme.sh also keeps settings in account.conf, which caught me out in the drill.

Monitoring and failure signals

  • cert-manager: Prometheus metrics plus Events and conditions on each object.
  • Certbot, lego, acme.sh, step clients: exit codes and logs. Nothing tells you if the timer stopped.
  • All: an external check of the served certificate is still needed, because a renewed file that was never loaded looks perfect in every log.

Release rhythm and upgrade risk

  • cert-manager: about every four months, with breaking Helm changes in the notes. 1.21.0 shipped with a known issue: renewal.policy: Disabled crash-loops the controller.
  • Certbot: a minor release every month or two; plugin packaging changed in 5.5.0.
  • step-ca: a few releases a year, security fixes in minor versions.
  • lego: frequent releases; v5 renamed flags, so v4 scripts need review. acme.sh: can upgrade itself from cron unless you use the packaged mode.

Worked example for the Ludhiana scenario

The company’s trouble was never the CA. It had a renewal with no reload, a vendor certificate with no owner, and no check from outside.

Step 1: make an inventory before choosing tools. List every name and IP address that serves TLS, from the dealer portal to the MQTT broker and the ERP appliance, with its issuer, lifetime, reload method and the person who would notice an expiry. Cross-check public names in Certificate Transparency search. This list becomes the monitoring list in step 6.

Step 2: VMs keep Certbot, with the missing job added. On both nginx VMs, Certbot 5.8.0 gets a deploy hook that tests and reloads nginx; systemctl list-timers must show the timer, and a dry run runs monthly. Switch to --preferred-profile tlsserver now, which rehearses the 2028 default of 45 days today. Then search for numbers that assume 90 days, as Let’s Encrypt itself suggests:

grep -rnE '\b(83|80|60|30)\b' /etc/cron* /etc/systemd/system /usr/local/bin /opt/scripts 2>/dev/null | grep -iE 'cert|renew|acme|ssl'
certbot certificates
certbot renew --dry-run --run-deploy-hooks

Step 3: Kubernetes gets cert-manager 1.21.2. One ClusterIssuer for Let’s Encrypt with profile: tlsserver, HTTP-01 through the existing ingress class for the API and catalogue, and Certificates with renewBeforePercentage rather than a fixed renewBefore. Use 1.21.2. The 1.20.4 line also has the June RBAC fix, but its notes warn that scanners will still flag three golang.org/x/crypto findings that do not affect cert-manager, and point to 1.21 for a clean scan. Leave ACMEUseARI off in production until it has run in a test cluster against Let’s Encrypt staging. Do not set renewal.policy: Disabled on 1.21.0. If a wildcard is ever needed, give the DNS-01 solver a token that can edit only _acme-challenge records, if the DNS provider allows it.

Step 4: the shop floor gets its own CA. The label gateway, the production tracking server and the MQTT broker do not need public trust; they need certificates that renew without a person. Run step-ca 0.30.2 on a small VM, keep the root key offline, put an authority policy that allows only the company’s internal domain, and give each machine a 72-hour certificate through the ACME provisioner, renewed by Certbot or lego with a reload hook, or by step ca renew --daemon where a client cannot run ACME. Distribute the root to the machines once, through the existing configuration management. Do not enable SCEP unless something needs it, and if it does, stay on 0.30.0 or later.

Step 5: the ERP appliance becomes a project, not a reminder. Its commercial certificate already cannot exceed 200 days, so the annual manual renewal is now at least two a year, four from March 2027 and about eight from March 2029. Ask the vendor for ACME or an API in this year’s support renewal. Until then, put a reverse proxy that does ACME in front of the appliance for users, and keep the vendor’s certificate on the internal side.

Step 6: prove it from outside. A small external check reads the certificate each name actually serves and alerts when less than a third of its lifetime is left, or when the serial did not change after a logged renewal. Internal names are checked from inside. This replaces the expiry emails Let’s Encrypt stopped in June 2025.

Step 7: tell the payment gateway nothing is pinned. Ask the gateway’s integration team whether they pin the portal’s certificate or intermediate. Let’s Encrypt began issuing from its new “Generation Y” hierarchy in November 2025, and the drill saw a YE2 intermediate on letsencrypt.org itself. Pinning a leaf or an intermediate turns every renewal or chain change into an outage.

Security and hygiene

Certificate tools hold keys, DNS credentials and the power to issue, and recent advisories show where that bites.

cert-manager. GHSA-8rvj-mm4h-c258, CVE-2026-62290 (26 June 2026, 12:18 AM, high): a namespace user who could create ACME Challenge resources directly could bypass DNS-01 solver selectors and make the controller use a ClusterIssuer’s DNS credentials with attacker-chosen settings; with acme-dns that leaked the API key. 1.18.0 to 1.20.2 affected, fixed in 1.19.6 and 1.20.3, and the cert-manager-edit role lost create on Challenges and Orders. GHSA-gx3x-vq4p-mhhv, CVE-2026-25518 (2 February 2026, 9:55 PM, medium): a crafted DNS response could crash the controller; fixed in 1.18.5 and 1.19.3. GHSA-r4pg-vg54-wxx4 (21 November 2024, low, no CVE): slow parsing of crafted PEM. 1.21.2 also caps ACME response bodies at 16 MiB and stops copying them into Events.

Certbot. The repository lists no security advisories. Its exposure is the host: root-owned keys under /etc/letsencrypt, DNS plugin credential files (keep them chmod 600), and hooks that run as root.

step-ca. GHSA-h8cp-697h-8c8p, CVE-2025-44005 (3 December 2025, 12:32 PM, critical): ACME and SCEP provisioners accepted a token claiming to be for them, so a request could skip the protocol’s authorisation; policies and webhooks still applied. Fixed in v0.29.0; blocking /sign was the stopgap. GHSA-j7c9-79x7-8hpr, CVE-2025-66406 (same day, medium): an SSHPOP token could revoke any SSH certificate. GHSA-q4r8-xm5f-56gw, CVE-2026-30836 (19 March 2026, 10:23 AM, critical): unsupported SCEP message types skipped authorisation; fixed in v0.30.0. GHSA-9qq8-cgcv-qmc9, CVE-2026-40097 (10 April 2026, 7:27 AM, low): a crafted attestation certificate could panic the CA with TPM device-attest-01; fixed in v0.30.0.

lego. GHSA-qqx8-2xmm-jrv8, CVE-2026-40611 (16 April 2026, 1:22 AM, high): with the webroot HTTP-01 provider, a malicious ACME server could send a token containing ../ and make lego write or delete files anywhere the process could; fixed in 4.34.0. GHSA-q82r-2j7m-9rv4, CVE-2025-54799 (6 August 2025, 7:54 PM, low): lego did not insist on HTTPS for the ACME directory and the URLs it returned; fixed in 4.25.2. Both are reminders that the CA is also an input you should not trust blindly.

acme.sh. No repository advisories. Release tags are signed from 3.1.5, which also added a packaged mode without self-update from cron. Self-updating shell code running as root is a supply chain decision; make it on purpose.

Habits that matter more than the version number

  • Run renewal clients as a dedicated user where possible, and give them write access only to the files they replace.
  • Scope DNS API tokens to the records the challenge needs, store them as secrets, and rotate them. Consider DNS-PERSIST-01 once your CA and client support it, and then protect the ACME account key as the real credential.
  • In Kubernetes, limit who can read Secrets and who can create cert-manager’s ACME resources.
  • For a private CA, keep the root offline, restrict names with policy, and disable provisioners you do not use.
  • Do not pin leaf certificates or intermediates in clients you control; pin to a root you manage, or not at all.
  • Watch the GitHub advisories of every tool in the chain, including the ACME library inside other software.

Decision guide

Lean towards cert-manager if

  • The certificates are consumed inside Kubernetes by ingress controllers, Gateway implementations, meshes or workloads.
  • You want public and private issuers behind one API, with metrics and Kubernetes-native status.
  • You can upgrade every four months or so and read the Helm breaking changes each time.
  • You accept that ARI is still alpha in 1.21, so its own two-thirds schedule is what you rely on today.

Lean towards Certbot if

  • The certificates live on a few VMs running nginx or Apache, and you want the installer to handle configuration and reloads.
  • You want a client run by a non-profit, with ARI on by default and profile selection with a safe fallback.
  • Your DNS provider is among Certbot’s DNS plugins, or HTTP-01 is enough.

Lean towards step-ca if

  • The names are private: shop floor devices, internal services, VPN endpoints, databases, SSH.
  • You want lifetimes of hours or days and passive revocation, and you can run a CA carefully: offline root, policies, backups and upgrades.
  • Your clients can speak ACME or use the step CLI. Pair it with cert-manager, Certbot or lego as clients.

Lean towards lego or acme.sh if

  • You need a DNS provider or deploy target only they support, a single static binary (lego), a shell script for routers and NAS boxes (acme.sh), or DNS-PERSIST-01 early.

Be careful whichever you pick

  • A renewal is not done until the new certificate is served and checked from outside.
  • Replace fixed day counts with “renew at about two thirds” or ARI before February 2027.
  • Test against Let’s Encrypt staging, scheduled for 64-day certificates from 14 October 2026, before production changes.

Ludhiana (invented) pick. Certbot 5.8.0 with a reload hook and the tlsserver profile on the portal VMs; cert-manager 1.21.2 in k3s with renewBeforePercentage and ARI off until tested; step-ca 0.30.2 with an authority policy and 72-hour certificates for the shop floor; an external check for every name; and ACME on the ERP vendor’s roadmap, with a proxy in front until then.

Checklist

  1. Every TLS endpoint is in one inventory with its issuer, lifetime, reload method and owner.
  2. No renewal schedule anywhere uses a fixed number of days that assumes 90-day or one-year certificates.
  3. Every renewal path has a reload step, and the reload is tested, not assumed.
  4. An external check alerts when a served certificate has less than a third of its life left.
  5. At least one public name already uses a 45-day certificate, so the 2028 default holds no surprises.
  6. ACME clients follow ARI where they support it, and run often enough to see a changed window within hours.
  7. DNS API tokens used for challenges are scoped, stored as secrets and rotated.
  8. cert-manager is on a supported line with the June 2026 fix (1.20.3, 1.19.6 or 1.21 and later).
  9. step-ca is on 0.30.0 or later, with unused provisioners disabled and the root key offline.
  10. Someone reads the CA/Browser Forum, Let’s Encrypt and tool release notes each month.

Common mistakes

  1. Renewing the certificate file and never reloading the server that reads it.
  2. Treating “Let’s Encrypt will email us” as monitoring; those emails ended in June 2025.
  3. Hard-coding --days, renewBefore or cron intervals that only work for 90-day certificates.
  4. Asking for --required-profile and having renewals fail when the CA retires the profile, instead of --preferred-profile with a fallback.
  5. Running the renewal client once a day or less and expecting it to react quickly to an ARI window after a revocation.
  6. Giving the DNS-01 solver a token that can rewrite the whole zone.
  7. Letting anyone with namespace edit rights create cert-manager Challenge objects on versions before the June 2026 fix.
  8. Placing a step-ca name policy on a provisioner in the self-hosted server, where it is not applied, and believing names are restricted.
  9. Pinning a leaf or intermediate certificate in a partner integration.
  10. Leaving an appliance on a manual, commercial certificate and discovering in 2027 that it now needs four renewals a year.

Drill on this box, 20 October 2026

Setting: a Debian 13 virtual machine shared with other jobs, with 8 vCPUs and 15 GiB of memory, of which about 2 GiB was free. Everything ran under /workspace/revisit/drill54, between about 2:26 PM and 2:41 PM IST, on loopback addresses and unprivileged ports only. Nothing talked to a public CA except two read-only fetches of the Let’s Encrypt directories. Timings are single runs and not a speed claim.

Install. GitHub release tarballs at 2:26 PM: Pebble v2.10.1, step-ca v0.30.2, step CLI v0.31.0, lego v5.5.2, acme.sh 3.1.6. Certbot 5.8.0 in a Python 3.13.5 venv (acme 5.8.0, cryptography 50.0.2, josepy 2.2.0).

Set-up. Pebble on 127.0.0.1:14000 with three profiles copying Let’s Encrypt’s lifetimes (90 days, 45 days, 160 hours), validating HTTP-01 on port 5002. step-ca on 127.0.0.1:19443 with an ACME provisioner and its testing flags --insecure --acme-http-port 5003. Identifiers were 127.0.0.1 and localhost, so no DNS change was needed. The core commands:

pebble -config pebble/config.json            # 127.0.0.1:14000, httpPort 5002
step ca init --deployment-type standalone --name "Drill54 Internal CA" \
  --dns localhost --address 127.0.0.1:19443 --provisioner drill-admin --acme
step-ca stepca/config/ca.json --insecure --acme-http-port 5003
certbot certonly --standalone --http-01-port 5002 --http-01-address 127.0.0.1 \
  --ip-address 127.0.0.1 --preferred-profile tlsserver --cert-name ip45
lego run --server https://localhost:19443/acme/acme/directory -d localhost \
  --http --http.address 127.0.0.1:5003 --path lego-stepca
acme.sh --issue -d 127.0.0.1 -w ./webroot --server https://localhost:14000/dir \
  --cert-profile tlsserver --ca-bundle pebble.minica.pem

Directories. The real Let’s Encrypt production and staging directories both listed classic, shortlived and tlsserver and a renewalInfo URL. The step-ca ACME directory listed only newNonce, newAccount, newOrder, revokeCert and keyChange: no renewalInfo and no profiles.

Certbot and profiles. The tlsserver certificate for 127.0.0.1 lasted 3,887,999 seconds, 45 days; first issuance took about 3 seconds. --required-profile tlsserver-2029 failed with “Order includes unrecognized profile name”. --preferred-profile tlsserver-2029 succeeded with whatever the CA chose as default, here a 160-hour certificate.

Certbot and ARI. An immediate certbot renew said “Certificate not yet due for renewal” after querying renewalInfo, which answered with Retry-After: 21600. I revoked the certificate at 2:28 PM. The next certbot renew still skipped: “Skipped ACME Renewal Info check because ari_retry_after 2026-10-20T14:58:01 is in the future” (the box clock runs on UTC, so 8:28 PM IST). Queried directly, the ARI window had moved into the past, meaning renew now. After I moved the stored ari_retry_after back to stand in for six hours passing, certbot renew found the certificate due, waited a “random delay of 459.56 seconds” because no terminal was attached, and renewed with a new serial at 2:36 PM.

lego. Against step-ca, lego got a 24-hour certificate in under a second. A second run warned “The server does not advertise a renewal info endpoint” and skipped for about 12 hours, half the lifetime. --profile tlsserver was silently ignored. Against Pebble, lego scheduled the next attempt about 30 days out; after lego certificates revoke, the next lego run renewed at once.

acme.sh. Against step-ca (72 hours), acme.sh planned renewal one day before expiry. Against Pebble, the ARI window was 18 to 20 November 2026 for the 45-day certificate (about day 29 to 31) and 18 to 20 December for the 90-day one (about day 59 to 61), and acme.sh saved a renewal time inside each. One trap: acme.sh stored the step-ca CA_BUNDLE in account.conf, and the next run against Pebble in the same home used that bundle and failed with curl error 60 despite --ca-bundle. A separate --home per CA fixed it.

step-ca lifetimes and policy. defaultTLSCertDuration of 72h on the ACME provisioner took effect after a SIGHUP reload. A name policy I placed on the provisioner was accepted and did nothing, as the documentation implies for the self-hosted server. At authority level, with localhost denied, the order failed with urn:ietf:params:acme:error:rejectedIdentifier, “The server will not issue certificates for the identifier”.

Public certificates. At 2:39 PM IST, letsencrypt.org, github.com, cert-manager.io, smallstep.com and eff.org served certificates valid for 90.0 days and www.google.com for 84.0 days, all far below the 200-day cap.

Clean-up. Pebble, step-ca and the temporary web servers were stopped at 2:41 PM; nothing outside the drill folder changed.

What this box could not test: cert-manager itself (no Kubernetes cluster or container runtime here), so its ARI gate, renewal windows and Secret handling are from the documentation only; issuance from any real public CA; DNS-01, DNS-PERSIST-01, TLS-ALPN-01 and wildcards; external account binding with commercial CAs; Certbot’s nginx and Apache installers; step-ca with MySQL, PostgreSQL, a KMS or an HSM, or in high availability; Windows; a real six-hour wait for Retry-After; and Let’s Encrypt staging’s 64-day certificates.

Unlearn

  • Unlearn that a certificate is a yearly purchase with a calendar reminder.
  • Unlearn that renewal is one step; it is get, put and prove.
  • Unlearn “renew 30 days before expiry” as a universal rule.
  • Unlearn that the CA will remind you before expiry.
  • Unlearn that internal names deserve ten-year self-signed certificates.

Re-learn

  • Re-learn the schedule: 200 days now, 100 from March 2027, 47 from March 2029, and Let’s Encrypt at 64 and then 45 days before that.
  • Re-learn renewal as a fraction of the lifetime, or as a window the CA suggests through ARI.
  • Re-learn reload hooks and external checks as part of every certificate, not an afterthought.
  • Re-learn DNS credentials and ACME account keys as secrets with real blast radius.
  • Re-learn a private CA as a small, careful service that removes manual work from internal TLS.

Closing

Shorter certificates are not a new problem; they make an old, hidden one visible. cert-manager 1.21.2 is the natural home for certificates that live in Kubernetes, with two-thirds renewal by default and ARI still behind an alpha gate. Certbot 5.8.0 remains the plain choice for a handful of web servers, now with ARI, profiles and IP address certificates. step-ca 0.30.2 turns internal TLS from a pile of long-lived self-signed files into short-lived, policy-checked certificates, provided you run it as carefully as any CA. lego and acme.sh fill the gaps for unusual DNS providers, routers and appliances. If the certificate sits on a Kubernetes edge, the post on moving off ingress-nginx covers where that Secret is read, and the post on Vault and OpenBao covers their PKI engines, another way to run a private CA, and a place for DNS tokens to live. Learn the three jobs of renewal. Unlearn the yearly reminder. Re-learn lifetimes as fractions. Practise on staging with 45-day certificates. Apply it before the next certificate expires on you.

Sources

Rules and certificate authorities

cert-manager

Certbot

step-ca

lego and acme.sh

Security records

Internal

comments powered by Disqus

Releted Posts

Vulnerability scanners in 2026: revisit Trivy, Grype and OSV-Scanner before you trust another green scan

Picture a small software company in Bhubaneswar that builds claims and pre-authorisation software for hospitals that deal with health insurance TPAs (an invented example, not a real client).

Read more

Lightweight Kubernetes in 2026: revisit k3s, k0s and Talos Linux before you stand up another small cluster

Picture a software company in Visakhapatnam that builds gate and yard software for container freight stations around the Visakhapatnam and Gangavaram ports, with two more customers at Kakinada and one inland depot near Vizianagaram (an invented example, not a real client).

Read more

Kubernetes secrets delivery in 2026: revisit External Secrets Operator, Sealed Secrets and SOPS before you commit another Secret YAML

Picture a platform team in Jaipur that runs a multi-tenant SaaS product on Kubernetes across Rajasthan and Delhi NCR (an invented example, not a real client).

Read more