cert-manager
The standard Kubernetes controller for X.509 certificates. Certificate and Issuer custom resources describe what is wanted; the controller generates keys, gets them signed by a CA, ACME server, Vault or a self-signed issuer, writes them to Secrets and renews them before expiry. Apache 2.0, a CNCF graduated project, free. It fits any cluster that needs internal TLS for databases and webhooks, or automated public certificates from Let's Encrypt.
cert-manager issues and renews TLS certificates inside a Kubernetes cluster. A Certificate object names the DNS names, the issuer and the Secret to fill; cert-manager does the rest and keeps the Secret valid for as long as the object exists. It removes the manual step that makes certificates expire: nobody has to remember to renew. The official docs at cert-manager.io/docs own the full API reference.
What it is
Three Deployments in the cert-manager namespace, a set of CRDs in the cert-manager.io and acme.cert-manager.io API groups, and an optional cmctl CLI:
- controller — runs the certificate, issuer, ACME and approval controllers, plus ingress-shim, which makes Certificates from annotated Ingress and Gateway objects.
- webhook — validates and mutates cert-manager resources and serves CRD conversion.
- cainjector — copies CA bundles into webhook configurations, CRDs and APIServices.
Licence is Apache 2.0. The project joined the CNCF Sandbox in 2020, moved to Incubating in 2022 and was announced as Graduated in November 2024. Related projects in the same organization: trust-manager (distributes CA bundles to namespaces), approver-policy (policy-based approval of certificate requests) and csi-driver (mounts certificates into pods without Secrets).
The current line is v1.21 (v1.21.2 shipped 2026-09-11; v1.20.4 is the other supported line). Releases come about every four months, each is supported until the second release after it, and only the last patch of each branch is supported. v1.22 is expected around November 2026. v1.21 supports Kubernetes 1.33 to 1.36.
v1.21 has three breaking changes, all in the Helm chart. The chart no longer creates the serviceaccounts/token Role for the controller's own ServiceAccount. The cert-manager-edit ClusterRole no longer grants create on Challenges or write access to Orders (already shipped in v1.20.3 and v1.19.6). The values prometheus.servicemonitor.targetPort, prometheus.servicemonitor.path and prometheus.podmonitor.path are removed, and the metrics port is renamed http-metrics. v1.21 also added experimental ACME Renewal Information (RFC 9773) behind the ACMEUseARI feature gate.
How it works
Everything is a custom resource. An Issuer (namespaced) or ClusterIssuer (cluster-wide) describes a signer: selfSigned, ca (a CA key pair in a Secret), acme, vault or an external issuer plugin. A Certificate describes the wanted certificate and the Secret to store it in. For each issuance the controller creates a CertificateRequest that carries the CSR. The internal approver approves requests for the built-in issuer types automatically. ACME issuers add Order and Challenge objects for the HTTP-01 or DNS-01 validation.
By default a certificate lasts 90 days and renews 2/3 of the way through its life. Since v1.18 the default privateKey.rotationPolicy is Always, so every renewal also generates a new key.
Step 1 of 4. A new Certificate has no valid Secret, so the trigger controller sets the Issuing condition. The key manager generates a private key and holds it in a temporary Secret.
CA issuer path. An ACME issuer adds Order and Challenge objects between the request and the signed certificate.
cert-manager bootstraps its own webhook the same way. At startup the webhook generates a self-signed key pair, stores it in a Secret, and cainjector injects that CA from the Secret. That path does not need cert-manager's own CRDs or webhook to work first.
When to use it
Reach for it when:
- Services inside the cluster need TLS from a private CA: databases, message brokers, admission webhooks, mTLS between services.
- Public names need Let's Encrypt or another ACME CA, renewed with no manual step.
- A Helm chart for another tool (Rancher, operators with webhooks) lists cert-manager as a prerequisite.
- Certificates must come from Vault, a cloud private CA or an internal PKI through one Kubernetes API.
Skip it when:
- All TLS terminates outside the cluster (a CDN or a cloud load balancer) and nothing inside needs a certificate.
- The ingress controller's built-in ACME client covers the only need, and only one instance requests certificates.
- An operator already manages its own certificates well, and a second source of truth would only add moving parts.
At a glance
Core concepts
- Issuer / ClusterIssuer — a signer configuration. A ClusterIssuer reads its Secrets from the cluster resource namespace (
cert-managerby default). - Certificate — the desired state: names, duration,
renewBeforeorrenewBeforePercentage, key algorithm,secretTemplate. - CertificateRequest — one signing request. Its
Approved,DeniedandReadyconditions show where issuance stopped. - Order / Challenge — the ACME objects for one issuance and one domain validation.
- ingress-shim — the
cert-manager.io/cluster-issuerannotation on an Ingress or Gateway creates a Certificate for its TLS hosts. - cainjector annotations —
cert-manager.io/inject-ca-from(a Certificate),cert-manager.io/inject-ca-from-secret(a Secret withcert-manager.io/allow-direct-injection: "true"),cert-manager.io/inject-apiserver-ca.
Key facts (v1.21)
| Item | Value |
|---|---|
| Licence | Apache 2.0 |
| Latest release | v1.21.2 (2026-09-11) |
| Kubernetes | 1.33 to 1.36 |
| Support window | Until the second following minor; last patch only |
| Default duration | 90 days |
| Default renewal | 2/3 of the duration |
| Default key rotation | Always (since v1.18) |
| Default container UID/GID | 65532 (since v1.20) |
| Chart | oci://quay.io/jetstack/charts/cert-manager |
Where certificates come from
| Aspect | cert-manager | Ingress controller ACME | CDN edge TLS |
|---|---|---|---|
| Covers | Any Secret in the cluster | Hostnames on that ingress | Public hostnames on the CDN |
| Private CA | Yes (ca, vault, external) |
No | No |
| Webhook and CRD CA bundles | Yes (cainjector) | No | No |
| Certificates usable by other pods | Yes, as Secrets | Usually not | No |
How to integrate
Install
helm install cert-manager oci://quay.io/jetstack/charts/cert-manager \
--version v1.21.2 \
--namespace cert-manager --create-namespace \
--set crds.enabled=true
kubectl -n cert-manager rollout status deploy/cert-manager-webhook
cmctl check api --wait=2m
The legacy repo https://charts.jetstack.io still works and is updated a few hours after the OCI chart. Before an upgrade to v1.21, remove any of the deleted prometheus.* keys from the values file; the chart's schema rejects them.
Bootstrap a private CA
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-ca
namespace: cert-manager # ClusterIssuers read Secrets from here
spec:
isCA: true
commonName: internal-ca
subject:
organizations: [example] # avoid an empty issuer DN
secretName: internal-ca
duration: 87600h # 10 years
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned
kind: ClusterIssuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-ca
spec:
ca:
secretName: internal-ca
A server certificate from that CA
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: db-server
namespace: data
spec:
secretName: db-server-tls
dnsNames:
- db-rw.data.svc
- db-rw.data.svc.cluster.local
duration: 2160h # 90 days
renewBeforePercentage: 33
usages: [server auth, digital signature, key encipherment]
issuerRef:
name: internal-ca
kind: ClusterIssuer
Inject a CA into a webhook
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: my-webhook
annotations:
cert-manager.io/inject-ca-from: my-system/my-webhook-serving-cert
webhooks: [] # caBundle on each entry is filled in by cainjector
Check status
kubectl get certificates,certificaterequests -A
kubectl describe certificate db-server -n data
cmctl status certificate db-server -n data
cmctl renew db-server -n data # force a renewal now
In the GL stack
cert-manager v1.21.2 runs on every cluster in the stack. Its job is in-cluster trust. It issues the certificates behind Rancher's self-signed CA flow, the TLS certificates for CloudNativePG's Postgres clusters, and the serving certificates and CA bundles for admission webhooks.
Public TLS terminates at Cloudflare. Browsers that reach paiddaily.io, sagedaily.io and the other product domains see Cloudflare's edge certificates, so no ACME issuer and no public DNS-01 credentials have to live inside a cluster. That keeps cert-manager's scope to private certificates whose clients are other services in the same stack.
Gotchas
- The webhook must be up before anything else works. Right after install, creating an Issuer can fail with an error that calls
webhook.cert-manager.io. Wait for the webhook rollout, or runcmctl check api --wait. A namespace stuck inTerminatingafter an uninstall usually clears withkubectl delete apiservice v1beta1.webhook.cert-manager.io. - CRDs outlive the release. Since v1.15 Helm keeps the CRDs on
helm uninstall. Deleting the CRDs deletes every Certificate, Issuer and Order in the cluster. - New key on every renewal. Since v1.18
rotationPolicy: Alwaysis the default. Anything that pins a public key needsrotationPolicy: Neverset explicitly. - Mounted Secrets update; processes do not. The kubelet refreshes Secret volumes (not
subPathmounts), but a server that reads its certificate only at startup keeps the old one until restart. Use a reloader, or a server that watches its files. - Self-signed issuer DN. A selfSigned certificate with no
subjecthas an empty issuer DN, which is technically invalid. cert-manager emits aBadConfigevent; setspec.subject. - ClusterIssuer Secrets live in one namespace. A
caClusterIssuer looks for its Secret in the cluster resource namespace, not in the Certificate's namespace. - Trust distribution is a separate problem. cert-manager puts
ca.crtin each Secret, but clients in other namespaces need the CA too. trust-manager, or a copy managed in Git, solves that. - Run v1.21.1 or later. v1.21.0 crash-looped the controller when a Certificate set
renewal.policy: Disabled, and could leave Issuers stuck atInvalidSolver. Both were fixed in v1.21.1. - Token RBAC removed in v1.21. Issuers that use
serviceAccountRef.namepointing at the controller's own ServiceAccount need their own Role forserviceaccounts/token: create, or a dedicated ServiceAccount.
Risks
- Licence and stewardship. Apache 2.0 and CNCF graduated, with a published governance model and completed security and fuzzing audits. There is no maintainer LTS; vendors sell longer support for older lines.
- Upgrade cadence. Each line is supported for about eight months. Skipping releases means stepping through several minors and their Helm value changes.
- The CA key is a Secret. Anyone who can read Secrets in the cluster resource namespace can sign certificates the whole stack trusts. Restrict that RBAC as tightly as cluster-admin.
- A webhook in the critical path. If the cert-manager webhook is down, creates and updates of cert-manager resources fail. Renewals that are already scheduled still need the controller to be up before expiry; the
certmanager_certificate_expiration_timestamp_secondsmetric is the alert source.
Alternatives
- Ingress controller ACME (Traefik, Caddy) — pick it when the only need is public certificates for one ingress.
- CDN or cloud-managed certificates (Cloudflare, AWS ACM) — pick them when TLS ends at the edge and nothing in the cluster needs its own certificate.
- Operator-managed certificates — CloudNativePG and many operators can generate their own CA; pick that for a single operator with no wider PKI.
- Vault or step-ca as the CA — pick them for a PKI shared beyond Kubernetes; cert-manager can still sit in front of both as the in-cluster client.
Related
- Rancher — its default ingress certificate flow depends on cert-manager.
- CloudNativePG and Postgres — the database TLS it issues.
- Traefik and Cloudflare — the ingress and the edge where public TLS ends.
- Reloader — restarts workloads when a renewed Secret changes.
- OpenBao and External Secrets — the other side of secret handling in the stack.
- Prometheus — where certificate expiry metrics are scraped.
- Official: cert-manager docs, Supported releases, CA injector, Releases.
