cert-manager finally reads ARI, and it does nothing until you tell it to
cert-manager 1.21 shipped on July 8, 2026, and the line everyone pulled out of the release notes was ARI. After years of the ecosystem talking about ACME Renewal Information, the most widely deployed certificate controller in Kubernetes can now ask the CA when it actually wants a certificate renewed, instead of guessing from notAfter and a fixed renewBefore.
Here is the part that got lost in the excitement: it is off. ARI support in 1.21 is experimental and sits behind the ACMEUseARI feature gate, which is disabled by default. You upgrade, nothing changes, and your certificates renew on exactly the schedule they did before. If you thought the upgrade alone bought you mass-revocation protection, it did not.
We have written before about why ARI is the only channel a CA has to tell you a deadline moved. This post is the other half of that story: the tool most of you use to act on ARI can now do it, and the upgrade that gets you there has sharp edges the announcement blog glossed over.
Turning it on is one flag, and one flag only
ARI lives behind a feature gate on the controller. You enable it the usual way:
# Helm values.yaml
featureGates: "ACMEUseARI=true"
Or straight on the controller if you run it without Helm:
--feature-gates=ACMEUseARI=true
Once it is on, cert-manager queries the ACME server's renewalInfo resource (RFC 9773) and uses the window the CA hands back to decide when to renew. Let's Encrypt is the obvious consumer here. During a mass revocation or a CA key rollover, they can shrink that window to "renew now" and your controller picks it up on its next poll, without anyone emailing you.
This matters more every year, not less. With certificate lifetimes marching toward 47 days by 2029, the renewal loop stops being a background chore and becomes the thing that keeps your sites up. A controller that renews on the CA's schedule instead of a hardcoded guess is the whole point. Turn the gate on. Then go read the rest of this, because the upgrade to 1.21 is not free.
The RBAC change that quietly stops token creation
Three breaking changes ship with 1.21, and the first one is the kind that fails at runtime instead of at helm upgrade, which makes it worse.
The Helm chart no longer creates a default Role and RoleBinding granting the controller create on serviceaccounts/token for its own ServiceAccount. The maintainers pulled it because no documented workflow needs it anymore; the Route53 docs section that justified it was deleted back in 2024.
If you point serviceAccountRef.name at the cert-manager controller's own ServiceAccount for issuer auth, that permission just disappeared. Issuance keeps working until the next token needs minting, then it stops. The fix is to stop borrowing the controller's identity and give the issuer its own:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cert-manager-token-creator
namespace: cert-manager
rules:
- apiGroups: [""]
resources: ["serviceaccounts/token"]
verbs: ["create"]
resourceNames: ["your-dedicated-issuer-sa"]
A dedicated ServiceAccount is the recommended path anyway. If you were leaning on the controller's, treat this upgrade as the nudge to stop.
cert-manager-edit lost create on Challenges and Orders
The second breaking change is smaller but catches people with automation. The aggregate cert-manager-edit ClusterRole no longer grants create on challenges.acme.cert-manager.io, nor create, patch, or update on orders.acme.cert-manager.io. That is GHSA-8rvj-mm4h-c258 — these are internal resources in cert-manager's own ACME workflow, and nothing outside the controller has any business creating them.
Nothing outside the controller, except the scripts people wrote anyway. If you have tooling that pokes Order or Challenge objects directly, it loses permission on upgrade and you grant it back explicitly. Worth knowing: this one already shipped in 1.20.3 and 1.19.6, so if you are coming from a recent patch it is not new to you. patch and update on Challenges are still there, because you occasionally need them to clear a stuck finalizer.
The metrics values that fail helm upgrade before it starts
The third one is the friendly kind, because it breaks loudly and immediately. Three Helm values are gone: prometheus.servicemonitor.targetPort, prometheus.servicemonitor.path, and prometheus.podmonitor.path. The metrics port on the controller Service was renamed from tcp-prometheus-servicemonitor to http-metrics at the same time.
Because the chart's values schema is additionalProperties: false, leaving any of those three keys in your overrides throws a validation error and the upgrade refuses to run. Annoying, but it fails before anything changes in the cluster, which is exactly the failure mode you want. Grep your overrides first:
grep -E 'servicemonitor\.(targetPort|path)|podmonitor\.path' values.yaml
Delete the hits, and if you scrape metrics by port name, update your ServiceMonitor to http-metrics.
The three bugs the .0 release shipped with
Here is my actual advice: do not run 1.21.0 in production. It is not broken in some vague, hand-wavy way. It went out with three specific known issues that the .1 release exists to fix, and one of them crash-loops your controller.
If a Certificate sets spec.renewal.policy: Disabled — a field from the new renewal policies feature in this same release — the trigger controller hits a nil pointer dereference and the process crash-loops. pki.RenewalTime() returns nil for that policy and the caller dereferences it without checking. One Certificate with that field takes the whole controller down. Until you are on 1.21.1, do not set renewal.policy: Disabled on anything, and if a controller is already crash-looping, that field is the first place to look.
The second is subtler and nastier for anyone running DNS-01. New eager validation means an Issuer that references a solver Secret which does not exist yet correctly reports Ready: False with reason InvalidSolver. Fine so far. The trap: create the missing Secret afterward and nothing happens. The controller's Secret-watch was never taught to recognise solver Secrets, so it will not re-reconcile until the next ten-hour informer resync, a spec change, or a restart. If you build issuers and their credential Secrets in separate steps — most GitOps pipelines do — you can land in a stuck state that looks permanent. Poke the Issuer spec to force reconciliation, or upgrade past it. This is the same class of silent-stall problem we covered in debugging cert-manager issuance loops.
The third is cosmetic: a generics regression spams the logs with type-assertion failures for every non-cert-manager Secret event, multiplied across seven sub-controllers. It does not affect issuance, but it makes your logs useless right when you might be trying to debug the other two.
Why the version you actually want is 1.21.2
All three are fixed in 1.21.1 (July 29). So skip 1.21.0 and go straight there. Then go one further, to 1.21.2 (September 11), and here is why.
1.21.2 fixes a renewal bug that is pure comedy until it is you: certificates whose renewal lands on a February 29 cron schedule were miscomputed across non-leap century years. It also stops the ACME and Vault issuers from copying untrusted HTTP response bodies into status conditions and Kubernetes Events — a real information-disclosure fix, since a redirect could otherwise surface internal response contents in Challenge.status.reason. Add data-race fixes in the HTTP-01 self-check and the scheduler, a validating-webhook panic on requests missing optional fields, and a bump to Go 1.26.8. The release notes say "all users should upgrade," and for once that is not boilerplate.
Two smaller things in 1.21 are worth turning on while you are in there. waitInsteadOfSelfCheck lets a solver skip cert-manager's own propagation check and just wait a fixed duration before asking the CA to validate. It is the escape hatch you want in split-horizon DNS and NAT-hairpin setups, which is where DNS-01 solver checks tend to fall apart. And the new Modern2026 PKCS#12 profile encodes with AES-256 and SHA-256 instead of legacy 3DES and RC2, which you need if anything in your chain is FIPS 140-3.
An upgrade order that won't page you
Put it together and the sequence is boring, which is the goal:
# 1. audit your overrides for the removed metrics keys
grep -E 'servicemonitor\.(targetPort|path)|podmonitor\.path' values.yaml
# 2. find Certificates that would trip the crashloop (only relevant pre-1.21.1)
kubectl get certificate -A -o json \
| jq -r '.items[] | select(.spec.renewal.policy=="Disabled") | .metadata.namespace+"/"+.metadata.name'
# 3. go straight to 1.21.2, CRDs first
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.2/cert-manager.crds.yaml
helm upgrade cert-manager jetstack/cert-manager -n cert-manager --version v1.21.2 -f values.yaml
# 4. watch it come up before you trust it
kubectl -n cert-manager rollout status deploy/cert-manager
kubectl -n cert-manager logs deploy/cert-manager --tail=50
Then, and only as a separate change once the upgrade is settled, flip ACMEUseARI=true and watch a renewal actually happen. Do not do both in one go. When something misbehaves you want to know whether it was the version or the feature, not both at once.
Frequently asked questions
Does upgrading to cert-manager 1.21 turn on ARI automatically?
No. ARI support is experimental and gated behind ACMEUseARI, which is off by default. You upgrade, nothing about renewal timing changes, and you have to set the feature gate explicitly before the controller starts reading the CA's renewalInfo endpoint.
Should I deploy 1.21.0 or wait for a patch?
Skip 1.21.0. It shipped with a controller crash-loop when a Certificate sets renewal.policy: Disabled, an Issuer that gets stuck at InvalidSolver after a solver Secret is created late, and log spam. 1.21.1 fixes all three and 1.21.2 adds security and renewal fixes on top, so go straight to 1.21.2.
Why did my issuer stop authenticating after the upgrade?
Most likely the removed serviceaccounts/token: create RBAC. The chart no longer grants the controller permission to mint tokens for its own ServiceAccount, so if your issuer used serviceAccountRef.name pointing at the controller SA, token creation fails at the next renewal. Give the issuer a dedicated ServiceAccount with its own Role instead.
Does ARI replace my renewBefore setting?
When the gate is on and the CA returns a renewal window, cert-manager uses that window to schedule renewal, which effectively overrides your static renewBefore for that certificate. If the CA does not support ARI or the query fails, it falls back to your configured behaviour, so keep renewBefore sane as a floor.