cert-manager Certificate Controller Deep Dive
The Certificate controller is the core component of cert-manager responsible for managing the lifecycle of X.509 certificates in Kubernetes. Rather than a single monolithic controller, cert-manager splits certificate management across four specialized sub-controllers that coordinate through status conditions and annotations on the Certificate resource.
1. What This Component Does
The Certificate controller ensemble manages the complete lifecycle of a Certificate resource, from initial key generation through issuance, renewal, and secret management. Its responsibilities include:
- Private Key Management: Generating and rotating cryptographic private keys (RSA, ECDSA, Ed25519) according to the Certificate spec.
- Issuance Triggering: Determining when a certificate needs to be issued or renewed and creating the appropriate CertificateRequest.
- Secret Management: Storing the signed certificate, private key, and CA chain in the target Kubernetes Secret in the correct format.
- Status Reporting: Maintaining accurate status conditions on the Certificate resource, including readiness, expiration time, and renewal time.
- Automatic Renewal: Proactively triggering certificate renewal before expiry based on the
renewBeforeconfiguration.
When/Why Use It?
The Certificate controller is invoked whenever a user creates, updates, or needs renewal of a Certificate resource. It is the primary workflow that cert-manager users interact with, either directly by creating Certificate resources or indirectly through the ingress-shim or gateway-shim.
2. Sub-Controller Architecture
The Certificate lifecycle is split across four controllers, each responsible for a distinct phase:
graph TD
CERT[Certificate Resource] --> KM[certificates-key-manager]
CERT --> TR[certificates-trigger]
CERT --> IS[certificates-issuing]
CERT --> RD[certificates-readiness]
KM -->|Ensures private key<br>exists in Secret| SEC[Target Secret]
TR -->|Creates when<br>issuance needed| CR[CertificateRequest]
IS -->|Copies signed cert<br>to Secret on completion| SEC
RD -->|Updates status<br>conditions| CERT
CR -->|Processed by| ISSUER[Issuer Controller]
ISSUER -->|Signs and returns cert| CR
certificates-key-manager
- File:
pkg/controller/certificates/keymanager/keymanager.go - Responsibility: Ensures a private key exists in the target Secret before any CertificateRequest is created. Generates a new key if one does not exist, or if the key specification has changed (e.g., switching from RSA to ECDSA).
- Key Logic: Reads the Certificate’s
spec.privateKeyconfiguration (algorithm, size, rotation policy) and ensures the Secret contains a matching key. IfrotationPolicy: Alwaysis set, generates a new key for each issuance.
certificates-trigger
- File:
pkg/controller/certificates/trigger/trigger.go - Responsibility: Evaluates whether a Certificate needs a new CertificateRequest. This is the decision-maker for initial issuance, renewal, and re-issuance after spec changes.
- Trigger Conditions:
- No signed certificate exists in the target Secret.
- The current certificate does not match the Certificate spec (e.g., DNS names changed).
- The renewal time has been reached (calculated from
NotAfter - renewBefore). - The Certificate has been manually triggered via the
cert-manager.io/issuingannotation.
- Key Logic: Compares the desired state (Certificate spec) with the actual state (contents of the Secret) and creates a CertificateRequest with the CSR when issuance is needed.
certificates-issuing
- File:
pkg/controller/certificates/issuing/issuing.go - Responsibility: Watches for completed CertificateRequests and copies the signed certificate into the target Secret. Handles the transition from “issuing” to “ready” state.
- Key Logic:
- Watches CertificateRequests owned by a Certificate.
- When a CertificateRequest reaches a
Ready=Truecondition, extracts the signed certificate chain. - Updates the target Secret with
tls.crt(certificate + chain),tls.key(private key), andca.crt(CA certificate). - Sets the
cert-manager.io/certificate-name,cert-manager.io/issuer-name, and other metadata annotations on the Secret. - Cleans up old CertificateRequests based on the
revisionHistoryLimitsetting.
certificates-readiness
- File:
pkg/controller/certificates/readiness/readiness.go - Responsibility: Continuously evaluates the state of the Certificate and its associated Secret, updating status conditions.
- Key Logic:
- Reads the certificate from the target Secret and parses its X.509 properties.
- Sets the
Readycondition based on whether a valid, non-expired certificate exists. - Calculates and reports
status.notBefore,status.notAfter, andstatus.renewalTime. - Emits Kubernetes Events for certificate expiration warnings.
3. Certificate Issuance Flow
Here is the detailed internal flow for a certificate issuance:
sequenceDiagram
participant User
participant K8s as API Server
participant KM as key-manager
participant TR as trigger
participant IS as issuing
participant RD as readiness
User->>K8s: Create Certificate
KM->>K8s: Read Certificate spec
KM->>K8s: Generate private key
KM->>K8s: Store key in Secret
TR->>K8s: Read Certificate + Secret
TR->>TR: Determine issuance needed
TR->>K8s: Create CSR from private key
TR->>K8s: Create CertificateRequest
TR->>K8s: Set Certificate Issuing=True
Note over K8s: CertificateRequest processed by issuer controller...
IS->>K8s: Watch CertificateRequest
IS->>IS: CertificateRequest Ready=True
IS->>K8s: Update Secret (tls.crt, ca.crt)
IS->>K8s: Set Certificate Issuing=False
IS->>K8s: Increment revision annotation
RD->>K8s: Read Certificate + Secret
RD->>RD: Parse X.509 certificate
RD->>K8s: Set Ready=True condition
RD->>K8s: Set notAfter, renewalTime status
4. Private Key Management
cert-manager supports three private key algorithms:
| Algorithm | Spec Field | Default Size | Usage |
|---|---|---|---|
| RSA | spec.privateKey.algorithm: RSA | 2048 bits | Most compatible, widely supported |
| ECDSA | spec.privateKey.algorithm: ECDSA | P-256 | Smaller keys, faster TLS handshakes |
| Ed25519 | spec.privateKey.algorithm: Ed25519 | N/A | Modern, high performance |
Rotation Policies
Never(default): The private key is generated once and reused across renewals. The same key is used for all certificate re-issuances.Always: A new private key is generated for every issuance. Provides forward secrecy but means the Secret contents change on each renewal.
Key generation is handled in pkg/util/pki/generate.go, which provides functions for each algorithm type.
5. Secret Format
The target Secret created by the issuing controller follows a standard format:
apiVersion: v1
kind: Secret
metadata:
name: my-cert-tls
annotations:
cert-manager.io/certificate-name: my-cert
cert-manager.io/issuer-name: letsencrypt-prod
cert-manager.io/issuer-kind: ClusterIssuer
cert-manager.io/issuer-group: cert-manager.io
cert-manager.io/common-name: example.com
cert-manager.io/alt-names: example.com,www.example.com
cert-manager.io/ip-sans: ""
cert-manager.io/uri-sans: ""
type: kubernetes.io/tls
data:
tls.crt: <base64 certificate + chain>
tls.key: <base64 private key>
ca.crt: <base64 CA certificate>
The type: kubernetes.io/tls ensures compatibility with Kubernetes Ingress controllers and other TLS consumers. Additional output formats can be configured via spec.keystores for JKS and PKCS#12 formats.
6. Renewal Logic
The renewal mechanism is one of cert-manager’s most important features:
- Renewal Time Calculation:
renewalTime = notAfter - renewBefore. IfrenewBeforeis not set, it defaults to 2/3 of the certificate’s total duration. - Trigger Check: The
certificates-triggercontroller comparestime.Now()againstrenewalTimeon each reconciliation. - Renewal Execution: When renewal time is reached, a new CertificateRequest is created. The old certificate remains in the Secret and continues serving traffic until the new one is issued.
- Backoff on Failure: If issuance fails, cert-manager applies exponential backoff before retrying, preventing excessive load on the certificate authority.
Example Timeline (90-day Let’s Encrypt Certificate)
Day 0: Certificate issued (NotAfter = Day 90)
Day 60: Renewal triggered (default: 2/3 of 90 = 60 days)
Day 60: New CertificateRequest created
Day 60: New certificate issued, Secret updated
Day 150: Next renewal triggered
7. Configuration
Certificate Spec Fields
Key configuration options on the Certificate resource:
spec.secretName: Name of the Secret to store the certificate in (required).spec.issuerRef: Reference to the Issuer or ClusterIssuer (required).spec.dnsNames: List of DNS Subject Alternative Names (most common).spec.ipAddresses: List of IP SANs.spec.uris: List of URI SANs.spec.duration: Requested certificate duration (default: 2160h / 90 days).spec.renewBefore: How long before expiry to trigger renewal (default: 720h / 30 days).spec.privateKey.algorithm: Key algorithm (RSA, ECDSA, Ed25519).spec.privateKey.size: Key size for RSA (2048, 4096, 8192) or ECDSA curve (256, 384).spec.privateKey.rotationPolicy: Key rotation policy (Never, Always).spec.keystores: Additional output formats (JKS, PKCS#12).spec.revisionHistoryLimit: Number of old CertificateRequests to retain.
Common Patterns
Basic TLS certificate:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: web-tls
spec:
secretName: web-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- example.com
- www.example.com
mTLS client certificate with ECDSA:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: client-cert
spec:
secretName: client-cert-tls
issuerRef:
name: internal-ca
kind: Issuer
duration: 720h
renewBefore: 240h
privateKey:
algorithm: ECDSA
size: 256
rotationPolicy: Always
usages:
- client auth
dnsNames:
- service.internal
8. Extension Points
- External Issuers: Third-party projects can implement their own CertificateRequest controllers to handle signing for custom CA types. The Certificate controller does not need modification; it creates CertificateRequests that external issuers pick up and process.
- Keystores: The
spec.keystoresfield allows configuring additional output formats. Custom keystore types could be added by extending the issuing controller’s Secret encoding logic. - Post-Issuance Hooks: While not natively supported, external controllers can watch for Certificate
Ready=Trueevents to trigger downstream actions (e.g., restarting deployments, updating external systems).
By understanding the Certificate controller’s sub-controller architecture and coordination mechanism, developers can effectively debug issuance issues, contribute to the codebase, and build extensions that integrate with the certificate lifecycle.