Guide
cert-manager not renewing Certificate resource
Certificate resources stuck without renewal—check Ready conditions, renewalTime, issuer health, and GitOps drift.
Updated Apr 2, 2026 819 words
cert-manager did not enter renewal when Certificate never stayed Ready or renewalTime looks stuck in the future.
When this happens
TLS Secret notAfter is forty days out but no new Order appears in events. Large fleets see no new Orders because controller QPS throttling was lowered during a noisy neighbor incident on the apiserver. Paused namespaces during cost freezes sometimes leave Certificates without a matching reduction in monitoring coverage.
GitOps keeps reverting Certificate specs to an old issuer reference. Vertical pod autoscaler may evict controller during heavy reconcile peaks. Frozen cluster upgrades sometimes leave cert-manager webhooks on older API versions that reject new Certificate fields silently except in controller logs.
First response
Certificate resources stuck without renewal—check Ready conditions, renewalTime, issuer health, and GitOps drift.
Searches for cert-manager not renewing usually mean automated renewal failed, succeeded locally but never reached the TLS listener, or hit Let's Encrypt rate limits. The fix is almost always permissions, challenge reachability, or a missing reload—not buying a longer-lived commercial cert as the first move.
For cert-manager not renewing Certificate resource, read the ACME or panel error verbatim, run a dry-run renewal if your stack supports it, then confirm what certificate strangers receive on port 443 after you deploy. Certificate files on disk lie; only live handshakes matter for users.
Certificates in Kubernetes look fine until Ingress TLS secrets stop updating thirty days before notAfter because cert-manager never entered renewal. Check Certificate status.renewalTime and whether the spec requests a duration shorter than the issuer allows. Silent non-renewal often traces to paused Certificates or immutable field changes rejected by the webhook. Triage should capture timestamps, account URLs, and the last Boulder error type before infrastructure changes widen blast radius.
See also cert-manager ACME solver failed and Let's Encrypt certificate expired in Kubernetes.
Why renewal failed
Renewal starts around two-thirds of lifetime unless renewBefore changes the schedule. Compare status.revision on Certificate with Secret generation; large gaps imply the controller stopped writing even though ingress still serves aging bytes. GitOps policies must allow cert-manager fields to be authoritative on Certificate resources.
Paused controllers, webhooks, or RBAC denials silently stop reconciliation. If you use external issuers like step-ca, renewal semantics differ from Let's Encrypt—confirm renewalTime logic matches your CA documentation. Webhook latency budgets should be monitored to prevent silent reconcile starvation.
Ingress still mounts an old Secret volume until pods restart even after renewal succeeds. Run quarterly game-day renewals in staging copying production issuer shape. Escalate with timestamps, hostnames, and serial comparisons attached so the next engineer does not repeat already-failed renew attempts.
cert-manager renews when roughly two-thirds of the lifetime elapsed unless spec.renewBefore overrides. If the Certificate never becomes Ready, renewals stall. Failed Orders without alerting leave secrets untouched while clients approach expiry. Clarify in tickets whether failure is validation, issuance, deploy, or listener presentment—teams too often mix these phases when reading logs.
Not renewing is not solved by manually editing the Secret TLS data without fixing the Certificate spec—it will revert. It is not proof Let's Encrypt is unavailable if only one namespace fails. If Certificate age approaches notAfter without a new Order, GitOps drift or paused reconciliation is more likely than a sudden Let's Encrypt outage.
Common causes
These patterns drive most cert-manager not renewing tickets. They explain why automation logs look green while browsers or APIs still break.
Certificate spec referenced a misspelled secretName so the controller never wrote updates. Issuer became not ready after ACME account key migration.
Resource limits prevented cert-manager controller from reconciling large numbers of Certificates. GitOps reverted Certificate edits each sync, freezing old specs. Overly aggressive Certificate ownerReference edits can orphan resources so controllers stop reconciling without obvious events.
Feature gate disabled renewals during a botched upgrade. Clock skew on nodes made renewalTime appear far future. Certificate spec.isCA mis-set to true on a leaf Certificate blocks renewal controllers expecting TLS server profiles.
Diagnose
Read Certificate status.renewalTime and controller logs for reconcile errors. Export Certificate yaml.status and the live openssl notAfter in one comment; leadership reads only that summary during bridge calls.
kubectl describe certificate -n APP_NS
kubectl logs -n cert-manager deploy/cert-manager -c cert-manager-controller --tail=100
Inspect controller logs for reconcile errors on the namespace. Verify webhook configurations are not timing out. Confirm no strategic merge patch from Argo CD strips issuerRef or DNS names. Check validating webhook version skew between cert-manager and Kubernetes minor releases for silent rejections. Export a three-line summary for leadership: Certificate name, renewalTime in UTC, and whether an Order exists—anything else is noise during bridge calls.
Fix step-by-step
Follow this sequence for cert-manager not renewing certificate resource. Capture command output after each step so the next responder can pick up without rerunning destructive changes.
- Verify Issuer Ready and ACME account reachable.
- Check Certificate conditions and renewalTime.
- Ensure GitOps allows cert-manager fields to mutate.
- Fix webhook timeouts blocking Certificate updates.
- Trigger manual renew via cmctl annotation if appropriate.
- Roll ingress after Secret updates.
- Confirm live openssl notAfter improved.
- Export metrics for certificates nearing renewalTime without Orders.
- Archive redacted ACME logs and openssl transcripts in the ticket before closing.
- Schedule a follow-up dry-run before the next natural renewal window.
Verify the live certificate
OpenSSL
echo | openssl s_client -connect HOST:443 -servername HOST 2>/dev/null | openssl x509 -noout -dates -subject -issuer
Replace HOST with the public hostname on the certificate. Run the same command after any reload or deploy change; acceptance means notAfter moved forward and serial changed versus the incident-opening capture.
How to read the output
Decode kubectl get secret tls -o jsonpath certificate and compare notAfter to live openssl. If live is worse than secret, ingress is not mounting the refreshed secret volume yet. openssl against the live hostname while Certificate Ready is False proves user impact even when operators focus solely on controller reconcile logs.
curl
curl -vI --connect-timeout 10 https://HOST/ 2>&1 | grep -Ei 'expire|SSL certificate|subject:|issuer:'
Replace HOST with the public hostname users request.
Roll out and verify
Describe Certificate and Issuer; fix conditions blocking Ready. Bump renewBefore temporarily after fixing solver for a one-shot manual renewal.
Trigger renewal with cmctl renew or annotation if supported; watch Order succeed. Rolling restart ingress pods if they cache old certs. Temporarily lower ingress QPS during mass renewals after long outages to avoid thundering herds of new Orders.
Monitor all Certificates cluster-wide with kube-state-metrics. Document GitOps patterns that allow cert-manager to own Certificate fields.
What to automate
Track renewalTime minus now cluster-wide; page when no Order is created inside a defined window. Track days until renewalTime without a new Order across the cluster.
ssldays external expiry monitoring catches Secrets that stopped updating even when CRDs look idle. ssldays shows when secrets stopped updating even if CRD statuses lag.
Frequently asked questions
What renewBefore should I use?
Default is fine for 90-day certs; shorten temporarily after fixing solver issues for one-shot renew. Record renewalTime from Certificate status in UTC.
Why no events on Certificate?
Controller may be down or filtered; check cert-manager pod health and ClusterRole. Record renewalTime from Certificate status in UTC.
Does HPA affect renewal?
Not directly, but noisy pod churn can hide ingress using old secrets—verify mounted cert after renew. Record renewalTime from Certificate status in UTC.
Can I delete and recreate the Certificate?
Deletion triggers new issuance and may hit rate limits; private keys in secrets may be lost. Prefer fixing issuer and forcing renew while preserving the same secretName. Backup secret data before destructive actions and record why recreation was necessary.
Monitor this hostname on a schedule
Live certificate checks and renewal reminders — not guesswork from PEM files.