Using Let's Encrypt with cert-manager? Read This Before February 10

2026-11-03来源 dev.to KubernetesEnglish

On February 10, 2027 Let's Encrypt shortens its standard certificate lifespan from 90 days to 64. Authorization reuse shrinks from 30 days to 10. One year later it reduces further to 45 days and 7 hours. There is no way to opt out and no profile that maintains 90 days. Staging already changed on October 14, 2026.

Here is the difficult aspect. cert-manager serves as the standard TLS issuer for most Kubernetes clusters, and right now its documentation and announcements mention nothing regarding the change. Its support for ACME Renewal Information, the method Let's Encrypt states you ought to use, launched in July 2026 as an alpha feature gate disabled by default, and prior to October 23 it possessed a bug that generated five certificates in ten seconds before triggering the rate limit.

Most clusters will survive February without issue, by chance. The standard renewal calculation naturally scales with whatever lifetime the CA provides. The clusters that fail will be those where someone adjusted a setting in 2023 and ignored it, and they will not fail at expiry. They will fail at Let's Encrypt's rate limiter, with a 429, on a Friday.

Ten boxes. Check all ten and shut the tab.

The checklist

  • [ ] 1. No Certificate hardcodes renewBefore. Execute the audit command below. Every RENEW_BEFORE column must display <none>.
  • [ ] 2. cert-manager is version 1.21.3 or later. kubectl -n cert-manager get deploy cert-manager -o jsonpath='{.spec.template.spec.containers[0].image}'
  • [ ] 3. Your Helm values exclude prometheus.servicemonitor.targetPort, prometheus.servicemonitor.path or prometheus.podmonitor.path. The 1.21 chart rejects them.
  • [ ] 4. Vault and Route53 issuers continue to function after 1.21. The chart ceased granting the controller serviceaccounts/token create.
  • [ ] 5. A staging ClusterIssuer exists and has issued at least one certificate since October 14. Its status.notAfter minus notBefore equals 64 days, and status.renewalTime falls on day 43.
  • [ ] 6. ACMEUseARI=true is enabled in staging and status.acme.ari is filled on a certificate renewed after the upgrade.
  • [ ] 7. Your "certificate expires in 15 days" alert is removed, substituted by alerts for missed renewals and renewal loops.
  • [ ] 8. No pod mounts a TLS Secret using subPath. grep -rn subPath across your manifests.
  • [ ] 9. You understand what a cluster rebuild does to your certificates. Re-creating a Certificate without restoring its Secret is a new order. The fifth identical one in a week is rejected.
  • [ ] 10. Your DNS-01 provider can handle 5.7 validations per certificate per year today and 8.1 in 2028, because with 10-day authorization reuse every renewal re-validates.

The remainder of this post explains why each box exists.

Box 1: the renewBefore you set in 2023

cert-manager renews when one third of the issued lifetime remains. It reads notAfter from the certificate the CA actually returned, not from spec.duration. On February 10 the default renewal day shifts from 60 to 43 automatically. If you never modified renewBefore, you have already passed this box.

kubectl get certificate -A -o custom-columns=\ 'NS:.metadata.namespace,NAME:.metadata.name,RENEW_BEFORE:.spec.renewBefore,PCT:.spec.renewBeforePercentage,NOT_AFTER:.status.notAfter,RENEWAL:.status.renewalTime' 

If you did interact with it, a static duration holds a different meaning for each lifetime:

Pinned value 90-day cert 64-day cert 45-day cert
renewBefore: 360h renews day 75 day 49 day 30
renewBefore: 720h renews day 60 day 34 day 15
renewBefore: 1440h renews day 30 day 4 renews on every reconcile

The final row is the one that triggers your alerts. For 64-day certificates, a 60-day renewBefore results in a new order every 4 days, roughly 90 per year for each Certificate rather than 4. With 45-day certificates, it exceeds the total lifespan, renewalTime is already past at issuance, and the controller keeps re-ordering until Let's Encrypt returns 429 due to the 5-duplicates-per-week cap. The validation webhook fails to block you, since it compares renewBefore with spec.duration, which still defaults to 90 days, rather than checking against what the CA will issue.

The solution adjusts to whatever the CA provides:

apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: api-tls spec: secretName: api-tls issuerRef: name: letsencrypt-prod kind: ClusterIssuer dnsNames: - api.example.com # was: renewBefore: 720h renewBeforePercentage: 33 

These two fields cannot coexist. Remove the previous one.

Boxes 2 to 4: 1.21 is required and 1.21 breaks things

ARI support is available starting with 1.21.0. Do not operate 1.21.0, 1.21.1 or 1.21.2 with the gate enabled. Issue #8999: the stale ARI window of the prior certificate bypassed the notBefore check due to Let's Encrypt backdating notBefore by one hour, causing the controller to loop. Resolved in 1.21.3 on October 23, 2026 by storing status.acme.ari.certID. 1.19 has not been supported since July 8, 2026.

The 1.21 chart checks values against a schema. helm upgrade stops immediately if any of these remain in your values file:

# removed in 1.21, delete before upgrading prometheus: servicemonitor: targetPort: 9402 path: /metrics podmonitor: path: /metrics 

The controller Service port changed its name from tcp-prometheus-servicemonitor to http-metrics. A manually created ServiceMonitor matching the previous name collects no data after the upgrade, and no one sees it until box 7 alerts.

The chart also ceased generating the Role and RoleBinding that allowed the controller’s ServiceAccount to generate tokens. A ClusterIssuer using Vault Kubernetes auth, or Route53 with serviceAccountRef pointing at the controller SA, returns a forbidden error after the upgrade until you restore that Role or switch to a dedicated ServiceAccount.

Boxes 5 and 6: staging is already 64-day, use it

Staging has been issuing 64-day certificates since October 14. You can see what your cluster will do in February right now.

apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: server: https://acme-staging-v02.api.letsencrypt.org/directory email: ops@example.com privateKeySecretRef: name: letsencrypt-staging-account solvers: - http01: ingress: ingressClassName: nginx 

Next, enable ARI, starting with staging:

# cert-manager Helm values featureGates: "ACMEUseARI=true" 

When the gate is active, cert-manager selects a randomized time within the CA's recommended window and disregards renewBefore and renewBeforePercentage completely. Renewals coordinated by ARI are excluded from all Let's Encrypt rate limits, representing the most compelling reason to enable it. Two caveats: certificates issued prior to the upgrade lack a certID and are bypassed by ARI until their subsequent standard renewal, and the gate remains alpha with no published plan for 1.22.

kubectl get certificate api-tls -o jsonpath='{.status.acme.ari}{"\n"}' 

An empty result for a certificate renewed post-upgrade indicates the gate is inactive on the controller.

Box 7: your expiry alert is now noise

A 15-day expiry alert worked when renewal occurred 30 days before expiration. With a 64-day certificate, the default renewal is 21 days prior to expiry, which still clears 15. For a 45-day certificate with ARI, the suggested window may open at day 30, 15 days before expiry, causing your alert to trigger on healthy certificates monthly until someone silences it. Then it loses value when it counts.

Alert on the two items that truly fail: a renewal that was due and did not occur, and a renewal that occurs too frequently.

apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: cert-manager-renewal-cadence spec: groups: - name: cert-manager rules: - alert: CertificateRenewalOverdue expr: | certmanager_certificate_renewal_timestamp_seconds < time() - 86400 and certmanager_certificate_ready_status{condition="True"} == 1 for: 1h labels: { severity: warning } annotations: summary: "{{ $labels.namespace }}/{{ $labels.name }} passed renewalTime over 24h ago and has not renewed" - alert: CertificateRenewalLoop expr: | changes(certmanager_certificate_expiration_timestamp_seconds[7d]) > 3 labels: { severity: critical } annotations: summary: "{{ $labels.namespace }}/{{ $labels.name }} renewed more than 3 times in 7 days, check renewBefore and issue #8993" 

Three renewals in a week exceeds what a healthy 45-day certificate performs, and falls two short of the duplicate threshold.

Box 8: the renewal cert-manager cannot deliver

Renewal creates a new Secret. ingress-nginx adopts it via Lua without reloading, and is also retired with no security fixes since March 2026, which is a different post. NGINX Gateway Fabric and Envoy Gateway observe Secrets and push the new certificate. Traefik hot-reloads. The scenario that still hurts is your own pod mounting the Secret with subPath. That file never updates. The pod serves the old certificate until it restarts, and at 45 days that is a monthly incident instead of a quarterly one.

Boxes 9 and 10: the arithmetic nobody ran

90 days 64 days 45 days
Renewals per certificate per year 4 5.7 8.1
Authorization reuse 30 days 10 days 7 hours
Re-validations per certificate per year 4 5.7 8.1

Renewals with an identical set of names are exempt from the 50-per-registered-domain-per-week and 300-per-account limits, so the lifetime change alone does not push a healthy cluster into a rate limit. A cluster with 200 Certificates goes from about 15 orders a week to 33 at 64 days and 47 at 45 days, and Let's Encrypt waves them through. The limit that is not waived is 5 duplicate certificates per week per name set. Two things hit it: the renewBefore loop from box 1, and rebuilding a cluster from manifests without restoring the TLS Secrets, which re-orders every certificate as a fresh duplicate. Do that twice during an incident, plus a few retries, and the next rebuild fails on TLS for a week. ARI-coordinated renewals are exempt from this limit too, which is box 6 again.

Authorization reuse matters more than it sounds. Today a renewal within 30 days of a validation skips the challenge. At 10 days, and then 7 hours, every renewal runs DNS-01 or HTTP-01 again. If your DNS provider throttles API calls, or your HTTP-01 solver depends on an Ingress controller that is itself being replaced, that is where the February failures will actually come from.

The point

cert-manager will carry most clusters through February 10 because its default happens to be a ratio, not a number. That is luck, not readiness. The project has not written a line about the change, its ARI support is alpha and off, and the first version that has it working safely is ten days old. If you run Let's Encrypt through cert-manager, you have about 100 days to tick ten boxes, and the first one takes thirty seconds.

原文:https://www.a8q9.cn/en/article/using-let-s-encrypt-with-cert-manager-read-this-before-february-10-315fdf.html

相关文章