Certificate Management with cert-manager
Versiones compatibles: cert-manager 1.16+, Kubernetes 1.31, 1.32, 1.33 Última actualización: July 13, 2026
cert-manager es un controlador de certificados X.509 potente y extensible para Kubernetes. Automatiza la gestión y emisión de certificados TLS desde varias fuentes, incluyendo Let's Encrypt, HashiCorp Vault, Venafi y sistemas PKI privados.
Table of Contents
- Overview
- Architecture
- Installation
- Core Concepts
- Issuer Types
- EKS Integration Patterns
- AWS-Native Alternative: ACM + ACK
- Service Mesh Integration
- trust-manager
- Monitoring and Troubleshooting
- Best Practices
- Summary and References
Overview
What cert-manager Solves
La gestión manual de certificados en entornos Kubernetes presenta desafíos operativos importantes:
| Challenge | Impact | cert-manager Solution |
|---|---|---|
| Manual renewal | Service outages from expired certificates | Automatic renewal before expiry |
| Inconsistent processes | Security gaps and configuration drift | Declarative Certificate resources |
| Key management | Risk of key exposure | Automatic key generation and rotation |
| Multi-issuer complexity | Operational overhead | Unified interface for all CA types |
| GitOps incompatibility | Cannot version control secrets | Certificate CRs are GitOps-friendly |
Project Status
cert-manager es un proyecto graduado de CNCF, lo que indica madurez lista para producción:
- Primera publicación: 2017
- CNCF Sandbox: 2020
- CNCF Incubating: 2022
- CNCF Graduated: 2024
- Mantenedores activos de Venafi, Red Hat y la comunidad
- Más de 10 000 estrellas en GitHub y adopción generalizada en producción
Why Certificate Lifecycle Automation Matters
┌─────────────────────────────────────────────────────────────────────────┐
│ Certificate Lifecycle Without Automation │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Day 1: Generate CSR → Day 2: Submit to CA → Day 3: Receive cert │
│ Day 4: Configure application → Day 89: Forget about renewal │
│ Day 90: Certificate expires → Day 90: Production outage! │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ Certificate Lifecycle With cert-manager │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Day 1: Apply Certificate CR → cert-manager handles everything │
│ Day 60: Automatic renewal triggered → Zero intervention required │
│ Day 90: New certificate active → No outage, no manual work │
│ │
└─────────────────────────────────────────────────────────────────────────┘Architecture
Component Overview
cert-manager consta de tres componentes principales que trabajan juntos para gestionar los ciclos de vida de los certificados:
Component Responsibilities
| Component | Responsibility | Key Functions |
|---|---|---|
| Controller | Main reconciliation loop | Watches Certificate CRs, creates CertificateRequests, stores issued certs |
| Webhook | Admission control | Validates and mutates cert-manager resources |
| cainjector | CA bundle injection | Injects CA certificates into webhooks and API server |
Certificate Issuance Flow
Installation
Prerequisites
Antes de instalar cert-manager, asegúrate de tener:
- Versión del cluster Kubernetes 1.25+
kubectlconfigurado con acceso de administrador del cluster- Helm 3.x (para el método de instalación con Helm)
Installation with Helm (Recommended)
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Install cert-manager with CRDs
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.2 \
--set crds.enabled=true \
--set prometheus.enabled=true \
--set webhook.timeoutSeconds=30Production Helm Values
# cert-manager-values.yaml
crds:
enabled: true
keep: true
replicaCount: 2
podDisruptionBudget:
enabled: true
minAvailable: 1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
prometheus:
enabled: true
servicemonitor:
enabled: true
namespace: monitoring
webhook:
replicaCount: 2
timeoutSeconds: 30
resources:
requests:
cpu: 25m
memory: 32Mi
limits:
cpu: 100m
memory: 128Mi
cainjector:
replicaCount: 2
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 100m
memory: 256Mi
# For EKS with IRSA
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/cert-manager-role
# Global settings
global:
leaderElection:
namespace: cert-manager
logLevel: 2# Install with custom values
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.2 \
-f cert-manager-values.yamlInstallation with kubectl
# Install cert-manager manifests (includes CRDs)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
# Verify installation
kubectl get pods -n cert-managerVerify Installation
# Check all pods are running
kubectl get pods -n cert-manager
# Expected output:
# NAME READY STATUS RESTARTS AGE
# cert-manager-5d7f97b46d-xxxxx 1/1 Running 0 2m
# cert-manager-cainjector-7f694c4c58-xxxxx 1/1 Running 0 2m
# cert-manager-webhook-7cd8c769bb-xxxxx 1/1 Running 0 2m
# Check CRDs are installed
kubectl get crd | grep cert-manager
# Expected output:
# certificaterequests.cert-manager.io
# certificates.cert-manager.io
# challenges.acme.cert-manager.io
# clusterissuers.cert-manager.io
# issuers.cert-manager.io
# orders.acme.cert-manager.io
# Test with cmctl (optional)
# Install cmctl: https://cert-manager.io/docs/reference/cmctl/
cmctl check apiCore Concepts
Custom Resource Definitions (CRDs)
cert-manager introduce varios CRDs para gestionar el ciclo de vida de los certificados:
| CRD | Scope | Purpose |
|---|---|---|
| Certificate | Namespaced | Declares desired certificate properties |
| CertificateRequest | Namespaced | Represents a CSR bound to an issuer |
| Issuer | Namespaced | Defines how to obtain certificates (namespace-scoped) |
| ClusterIssuer | Cluster | Defines how to obtain certificates (cluster-wide) |
| Order | Namespaced | Represents an ACME order |
| Challenge | Namespaced | Represents an ACME challenge |
Certificate Resource
El recurso Certificate es la interfaz principal para solicitar certificados:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com-tls
namespace: default
spec:
# Secret where the certificate will be stored
secretName: example-com-tls-secret
# Certificate duration (default: 2160h = 90 days)
duration: 2160h
# Renewal window (default: 360h = 15 days before expiry)
renewBefore: 360h
# Subject fields
subject:
organizations:
- Example Inc
# Common name (deprecated, use dnsNames)
commonName: example.com
# Private key settings
privateKey:
algorithm: RSA
size: 2048
rotationPolicy: Always
# Usages
usages:
- digital signature
- key encipherment
- server auth
# DNS names for the certificate
dnsNames:
- example.com
- www.example.com
- api.example.com
# IP addresses (optional)
ipAddresses:
- 192.168.1.1
# Reference to the issuer
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
group: cert-manager.ioIssuer vs ClusterIssuer
# Issuer - namespace-scoped
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: ca-issuer
namespace: my-namespace # Only usable in this namespace
spec:
ca:
secretName: ca-key-pair
---
# ClusterIssuer - cluster-wide
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod # No namespace, available cluster-wide
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginxCertificateRequest Resource
CertificateRequests suelen ser creados automáticamente por cert-manager:
apiVersion: cert-manager.io/v1
kind: CertificateRequest
metadata:
name: example-com-tls-xxxxx
namespace: default
spec:
# Base64-encoded CSR
request: LS0tLS1CRUdJTi...
# Reference to the issuer
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
group: cert-manager.io
# Requested duration
duration: 2160h
# Usages
usages:
- digital signature
- key encipherment
- server authIssuer Types
SelfSigned Issuer (Development/Testing)
Los certificados autofirmados son útiles para entornos de desarrollo y pruebas:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
# Create a self-signed CA certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: selfsigned-ca
namespace: cert-manager
spec:
isCA: true
commonName: selfsigned-ca
secretName: selfsigned-ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.ioCA Issuer (Internal PKI)
Para organizaciones con su propia Certificate Authority interna:
# First, create a Secret with the CA certificate and key
apiVersion: v1
kind: Secret
metadata:
name: ca-key-pair
namespace: cert-manager
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi... # Base64-encoded CA certificate
tls.key: LS0tLS1CRUdJTi... # Base64-encoded CA private key
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: ca-issuer
spec:
ca:
secretName: ca-key-pair
---
# Request a certificate from the CA issuer
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-service-tls
namespace: default
spec:
secretName: internal-service-tls-secret
duration: 8760h # 1 year
renewBefore: 720h # 30 days
dnsNames:
- internal-service.default.svc.cluster.local
- internal-service.default.svc
- internal-service
issuerRef:
name: ca-issuer
kind: ClusterIssuerACME / Let's Encrypt
ACME (Automatic Certificate Management Environment) se usa con Let's Encrypt y otras CA compatibles con ACME.
ACME Challenge Types
HTTP-01 Solver
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Let's Encrypt production server
server: https://acme-v02.api.letsencrypt.org/directory
# Email for certificate expiry notifications
email: admin@example.com
# Secret to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod-account-key
# HTTP-01 solver configuration
solvers:
- http01:
ingress:
class: nginx
# Or specify a specific ingress name
# ingressTemplate:
# metadata:
# annotations:
# kubernetes.io/ingress.class: nginxDNS-01 Solver with Route53 and IRSA
# IAM Policy for cert-manager (create via AWS CLI or Terraform)
# {
# "Version": "2012-10-17",
# "Statement": [
# {
# "Effect": "Allow",
# "Action": "route53:GetChange",
# "Resource": "arn:aws:route53:::change/*"
# },
# {
# "Effect": "Allow",
# "Action": [
# "route53:ChangeResourceRecordSets",
# "route53:ListResourceRecordSets"
# ],
# "Resource": "arn:aws:route53:::hostedzone/HOSTED_ZONE_ID"
# },
# {
# "Effect": "Allow",
# "Action": "route53:ListHostedZonesByName",
# "Resource": "*"
# }
# ]
# }
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-dns01-account-key
solvers:
# DNS-01 solver for Route53
- selector:
dnsZones:
- "example.com"
dns01:
route53:
region: us-east-1
hostedZoneID: Z1234567890ABC
# Using IRSA - no credentials needed in the spec
# cert-manager ServiceAccount must have the IAM role annotation
---
# Wildcard certificate (only possible with DNS-01)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-example-com
namespace: default
spec:
secretName: wildcard-example-com-tls
dnsNames:
- "example.com"
- "*.example.com"
issuerRef:
name: letsencrypt-dns01
kind: ClusterIssuerAWS Private CA Issuer
Para entornos empresariales que requieren AWS Private Certificate Authority:
# Install AWS PCA Issuer
helm repo add awspca https://cert-manager.github.io/aws-privateca-issuer
helm install aws-pca-issuer awspca/aws-privateca-issuer \
--namespace cert-manager \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::ACCOUNT_ID:role/aws-pca-issuer-role# IAM Policy for AWS PCA Issuer
# {
# "Version": "2012-10-17",
# "Statement": [
# {
# "Effect": "Allow",
# "Action": [
# "acm-pca:IssueCertificate",
# "acm-pca:GetCertificate",
# "acm-pca:DescribeCertificateAuthority"
# ],
# "Resource": "arn:aws:acm-pca:REGION:ACCOUNT_ID:certificate-authority/CA_ID"
# }
# ]
# }
---
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAClusterIssuer
metadata:
name: aws-pca-issuer
spec:
arn: arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
region: us-east-1
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-mtls-cert
namespace: default
spec:
secretName: internal-mtls-tls
duration: 8760h
renewBefore: 720h
commonName: service.internal.example.com
dnsNames:
- service.internal.example.com
usages:
- digital signature
- key encipherment
- server auth
- client auth # For mTLS
issuerRef:
name: aws-pca-issuer
kind: AWSPCAClusterIssuer
group: awspca.cert-manager.ioHashiCorp Vault PKI
Para organizaciones que usan HashiCorp Vault como backend de PKI:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-issuer
spec:
vault:
# Vault server address
server: https://vault.example.com
# PKI secrets engine path
path: pki/sign/my-role
# Vault namespace (Enterprise only)
# namespace: admin
# CA bundle for Vault TLS
caBundle: LS0tLS1CRUdJTi...
# Authentication method
auth:
# Kubernetes auth method
kubernetes:
role: cert-manager
mountPath: /v1/auth/kubernetes
serviceAccountRef:
name: cert-manager
# namespace: cert-manager # Optional, defaults to issuer namespace
# Or use AppRole auth
# appRole:
# path: approle
# roleId: my-role-id
# secretRef:
# name: vault-approle-secret
# key: secretId
---
# Vault configuration (run in Vault)
# vault secrets enable pki
# vault secrets tune -max-lease-ttl=8760h pki
# vault write pki/root/generate/internal \
# common_name="Example Root CA" \
# ttl=87600h
# vault write pki/roles/my-role \
# allowed_domains="example.com" \
# allow_subdomains=true \
# max_ttl=72h
# vault write auth/kubernetes/role/cert-manager \
# bound_service_account_names=cert-manager \
# bound_service_account_namespaces=cert-manager \
# policies=pki-policy \
# ttl=1hEKS Integration Patterns
TLS Termination Comparison
| Approach | TLS Termination | Certificate Source | Use Case |
|---|---|---|---|
| ALB + ACM | At ALB | AWS Certificate Manager | Public-facing with AWS-managed certs |
| ALB + cert-manager | At ALB | cert-manager | Public-facing with custom CA |
| NLB + Ingress | At Ingress Controller | cert-manager | Layer 4 load balancing |
| NLB + Pod | At Pod | cert-manager | End-to-end encryption |
| Gateway API | At Gateway | cert-manager | Modern API, future-proof |
Los certificados ACM ahora también se pueden definir y reconciliar como recursos nativos de Kubernetes. Consulta AWS-Native Alternative: ACM + ACK más abajo.
ALB Ingress with ACM vs cert-manager
# Option 1: ALB with ACM (AWS-managed certificates)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress-acm
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
# ACM certificate ARN
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:123456789012:certificate/xxxxxxxx
alb.ingress.kubernetes.io/ssl-policy: ELBSecurityPolicy-TLS13-1-2-2021-06
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80
---
# Option 2: Ingress-nginx with cert-manager
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress-certmanager
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- app.example.com
secretName: app-example-com-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-service
port:
number: 80NLB with TLS Termination at Ingress Controller
# NLB Service for ingress-nginx
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
type: LoadBalancer
ports:
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app.kubernetes.io/name: ingress-nginx
---
# Certificate for ingress controller
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ingress-tls
namespace: ingress-nginx
spec:
secretName: ingress-tls-secret
dnsNames:
- "*.example.com"
- example.com
issuerRef:
name: letsencrypt-dns01
kind: ClusterIssuerGateway API Integration
# Install Gateway API CRDs
# kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
gatewayClassName: nginx # or istio, envoy, etc.
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- name: wildcard-example-com-tls
kind: Secret
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: default
spec:
parentRefs:
- name: main-gateway
namespace: default
hostnames:
- app.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-service
port: 80AWS-Native Alternative: ACM + ACK
Overview
El 15 de diciembre de 2025, AWS anunció la gestión automatizada de certificados para Kubernetes con AWS Certificate Manager (ACM), integrando ACM con AWS Controllers for Kubernetes (ACK). Con el controlador ACM ACK instalado en un cluster, los certificados se pueden definir como custom resources nativos de Kubernetes (YAML), y el controlador ACK gestiona automáticamente todo el ciclo de vida: solicitar la emisión, completar la validación de dominio/propiedad, y crear y renovar el Secret de Kubernetes correspondiente.
Mientras cert-manager es una solución open source de CNCF que admite una amplia variedad de issuers (Let's Encrypt y otros issuers ACME, Vault, AWS Private CA, autofirmados y más), la integración ACM+ACK es una alternativa nativa de AWS. Para organizaciones que ya han invertido en el ecosistema IAM/ACM, ofrece el mismo tipo de automatización sin operar un controlador open source separado.
July 2026 Update: ACM Now Supports the ACME Protocol
En julio de 2026, ACM agregó soporte para emitir certificados públicos mediante el protocolo ACME. Puedes aprovisionar un endpoint de servidor ACME completamente gestionado que emite certificados TLS públicos con una validez de 45 días desde Amazon Trust Services usando cualquier cliente compatible con ACMEv2, incluyendo Certbot, acme.sh y cert-manager para Kubernetes. En otras palabras, ahora puedes consumir certificados públicos de ACM desde el Issuer ACME existente de cert-manager simplemente apuntando su campo server al endpoint ACME de ACM, sin instalar el controlador ACK.
Los administradores de PKI pueden aplicar gobernanza centralizada a nivel de endpoint, restringiendo ámbitos de dominio y aplicando políticas de wildcard, y delegar solicitudes de certificados a equipos de aplicaciones sin distribuir credenciales DNS, con toda la actividad auditable mediante registros de CloudTrail y métricas de CloudWatch. Con el CA/Browser Forum exigiendo ciclos de vida de certificados de 47 días para 2029, la combinación cert-manager + endpoint ACME de ACM se posiciona como una alternativa nativa de AWS a Let's Encrypt.
Supported Certificate Types
| Type | Use Case |
|---|---|
| ACM Exportable Public Certificates | Public-domain certificates exported to a Kubernetes Secret for direct use by Pods/Ingress |
| AWS Private CA | Internal services and service-mesh (Istio, Linkerd) mTLS workloads that require a private PKI |
Applicable Scenarios
- Terminación TLS directamente en un Pod de aplicación (NGINX, aplicaciones personalizadas)
- Certificados de workload de service mesh (Istio, Linkerd)
- Ingress Controllers de terceros (NGINX Ingress, Traefik) donde no se usa integración de certificados nativa de ALB/NLB
- Entornos multi-cluster/híbridos que necesitan una gestión de certificados consistente
Example: Defining a Certificate via ACK
apiVersion: acm.services.k8s.aws/v1alpha1
kind: Certificate
metadata:
name: example-com-tls
namespace: default
spec:
domainName: example.com
subjectAlternativeNames:
- "*.example.com"
validationMethod: DNS
tags:
- key: managed-by
value: ackEl controlador ACK observa este recurso, solicita el certificado a ACM y crea/renueva el Secret de Kubernetes resultante una vez que se completa la emisión. Los nombres exactos de los campos y el mecanismo de exportación del Secret pueden variar según la versión del controlador ACM ACK, así que revisa la documentación oficial antes de instalar.
Comparison with cert-manager
| Aspect | cert-manager | ACM + ACK |
|---|---|---|
| Issuers | Let's Encrypt, Vault, AWS PCA, and more | ACM (public), AWS Private CA |
| Ecosystem | CNCF open source, vendor-neutral | AWS-native, IAM-based access control |
| What you install | cert-manager controller | ACK service controller for ACM |
| Cost | Free (infrastructure cost only) | Standard ACM/AWS Private CA pricing; no additional charge for the Kubernetes integration itself |
| Best fit | Multi-cloud, or ACME issuers required | AWS-centric organizations already using ACM/IAM |
Los dos enfoques no se excluyen mutuamente; por ejemplo, los certificados de dominios públicos pueden gestionarse mediante ACM+ACK mientras los certificados mTLS internos siguen usando cert-manager con AWS PCA Issuer.
Service Mesh Integration
Istio with istio-csr
istio-csr es un agente de cert-manager que se integra con Istio para proporcionar certificados de workload. Reemplaza la CA predeterminada de istiod con certificados firmados por cert-manager.
Installing istio-csr
# Create issuer for istio-csr
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: istio-ca
spec:
ca:
secretName: istio-ca-secret
EOF
# Install istio-csr
helm repo add jetstack https://charts.jetstack.io
helm install istio-csr jetstack/cert-manager-istio-csr \
--namespace cert-manager \
--set app.certmanager.issuer.name=istio-ca \
--set app.certmanager.issuer.kind=ClusterIssuer \
--set app.certmanager.issuer.group=cert-manager.io \
--set app.tls.certificateDuration=1h \
--set app.tls.istiodCertificateDuration=1h \
--set app.tls.rootCAFile=/var/run/secrets/istio-csr/ca.pemConfiguring Istio to use istio-csr
# IstioOperator configuration
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio
namespace: istio-system
spec:
profile: default
meshConfig:
# Use istio-csr for workload certificates
caCertificates:
- pem: |
# CA certificate from cert-manager
defaultConfig:
proxyMetadata:
# Point to istio-csr for certificate signing
ISTIO_META_CERT_SIGNER: istio-csr.cert-manager.svc
components:
pilot:
k8s:
env:
# Disable istiod CA
- name: ENABLE_CA_SERVER
value: "false"
# Use external CA
- name: EXTERNAL_CA
value: ISTIOD_RA_KUBERNETES_API
overlays:
- apiVersion: apps/v1
kind: Deployment
name: istiod
patches:
- path: spec.template.spec.containers[0].volumeMounts[-]
value:
name: istio-csr-ca-configmap
mountPath: /var/run/secrets/istiod/tls
readOnly: true
- path: spec.template.spec.volumes[-]
value:
name: istio-csr-ca-configmap
configMap:
name: istio-csr-ca-configmapLinkerd Trust Anchor Management
Linkerd requiere un certificado de trust anchor para mTLS. cert-manager puede gestionarlo:
# Create a self-signed issuer for the trust anchor
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: linkerd-trust-anchor
spec:
selfSigned: {}
---
# Trust anchor certificate (root CA)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-trust-anchor
namespace: linkerd
spec:
isCA: true
commonName: root.linkerd.cluster.local
secretName: linkerd-trust-anchor
privateKey:
algorithm: ECDSA
size: 256
duration: 87600h # 10 years
renewBefore: 8760h # 1 year
issuerRef:
name: linkerd-trust-anchor
kind: ClusterIssuer
---
# Issuer using the trust anchor
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: linkerd-identity-issuer
namespace: linkerd
spec:
ca:
secretName: linkerd-trust-anchor
---
# Identity issuer certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: linkerd-identity-issuer
namespace: linkerd
spec:
isCA: true
commonName: identity.linkerd.cluster.local
secretName: linkerd-identity-issuer
privateKey:
algorithm: ECDSA
size: 256
duration: 48h
renewBefore: 25h
issuerRef:
name: linkerd-identity-issuer
kind: Issuer# Install Linkerd with cert-manager managed certificates
linkerd install \
--identity-trust-anchors-file <(kubectl get secret linkerd-trust-anchor -n linkerd -o jsonpath='{.data.ca\.crt}' | base64 -d) \
--identity-issuer-certificate-file <(kubectl get secret linkerd-identity-issuer -n linkerd -o jsonpath='{.data.tls\.crt}' | base64 -d) \
--identity-issuer-key-file <(kubectl get secret linkerd-identity-issuer -n linkerd -o jsonpath='{.data.tls\.key}' | base64 -d) \
| kubectl apply -f -trust-manager
trust-manager es un proyecto complementario de cert-manager que distribuye bundles de confianza de CA entre namespaces.
Installing trust-manager
helm repo add jetstack https://charts.jetstack.io
helm install trust-manager jetstack/trust-manager \
--namespace cert-manager \
--set app.trust.namespace=cert-managerBundle Resource
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: public-bundle
spec:
sources:
# Include default CA certificates
- useDefaultCAs: true
# Include specific ConfigMap
- configMap:
name: my-ca-bundle
key: ca-bundle.crt
# Include from Secret
- secret:
name: internal-ca
key: tls.crt
# Inline CA certificate
- inlineString: |
-----BEGIN CERTIFICATE-----
MIIBkTCB+wIJAKHBfpEgcMFuMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBnJv
...
-----END CERTIFICATE-----
target:
# Create ConfigMap in all namespaces
configMap:
key: ca-bundle.crt
# Or specify namespaces
# namespaceSelector:
# matchLabels:
# trust-bundle: enabledUsing Trust Bundles in Applications
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-trust-bundle
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: ca-bundle
mountPath: /etc/ssl/certs/ca-certificates.crt
subPath: ca-bundle.crt
readOnly: true
env:
- name: SSL_CERT_FILE
value: /etc/ssl/certs/ca-certificates.crt
volumes:
- name: ca-bundle
configMap:
name: public-bundleMonitoring and Troubleshooting
Prometheus Metrics
cert-manager expone métricas para monitorizar la salud de los certificados:
# ServiceMonitor for Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: cert-manager
namespace: monitoring
spec:
selector:
matchLabels:
app.kubernetes.io/name: cert-manager
namespaceSelector:
matchNames:
- cert-manager
endpoints:
- port: tcp-prometheus-servicemonitor
interval: 30sKey Metrics
| Metric | Description | Alert Threshold |
|---|---|---|
certmanager_certificate_ready_status | Certificate ready state (1=ready, 0=not ready) | != 1 |
certmanager_certificate_expiration_timestamp_seconds | Certificate expiry timestamp | < 7 days |
certmanager_certificate_renewal_timestamp_seconds | Next renewal timestamp | Past due |
certmanager_controller_sync_call_count | Controller sync operations | Spike detection |
certmanager_http_acme_client_request_count | ACME HTTP requests | Rate limiting detection |
PrometheusRule for Alerting
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cert-manager-alerts
namespace: monitoring
spec:
groups:
- name: cert-manager
rules:
- alert: CertificateNotReady
expr: certmanager_certificate_ready_status == 0
for: 10m
labels:
severity: warning
annotations:
summary: "Certificate {{ $labels.name }} in {{ $labels.namespace }} is not ready"
description: "Certificate has been in not-ready state for more than 10 minutes"
- alert: CertificateExpiringSoon
expr: (certmanager_certificate_expiration_timestamp_seconds - time()) < 604800
for: 1h
labels:
severity: warning
annotations:
summary: "Certificate {{ $labels.name }} expires in less than 7 days"
description: "Certificate will expire in {{ $value | humanizeDuration }}"
- alert: CertificateExpiryCritical
expr: (certmanager_certificate_expiration_timestamp_seconds - time()) < 86400
for: 10m
labels:
severity: critical
annotations:
summary: "Certificate {{ $labels.name }} expires in less than 24 hours"
description: "Certificate will expire in {{ $value | humanizeDuration }}"Certificate Readiness Check
# Check certificate status
kubectl get certificates -A
# Detailed certificate status
kubectl describe certificate <name> -n <namespace>
# Check CertificateRequest status
kubectl get certificaterequests -A
# View certificate details
kubectl get secret <secret-name> -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -nooutCommon Errors and Solutions
| Error | Cause | Solution |
|---|---|---|
Waiting for HTTP-01 challenge propagation | Challenge endpoint not accessible | Check Ingress, Service, firewall rules |
DNS problem: NXDOMAIN | DNS record not created | Verify Route53 permissions, hosted zone ID |
Error presenting challenge: 403 Forbidden | IRSA/IAM permissions issue | Check ServiceAccount annotations, IAM policy |
acme: error code 429 | Rate limit exceeded | Wait 1 hour, use staging server for testing |
certificate is not valid for any names | DNS name mismatch | Verify dnsNames in Certificate spec |
Error getting keypair for CA issuer | CA secret missing or malformed | Check CA secret exists with correct keys |
cmctl CLI Tool
# Install cmctl
# Linux
curl -fsSL https://github.com/cert-manager/cmctl/releases/download/v2.1.0/cmctl_linux_amd64.tar.gz | tar xz
sudo mv cmctl /usr/local/bin/
# Verify API is ready
cmctl check api
# Check certificate status
cmctl status certificate <name> -n <namespace>
# Manually trigger renewal
cmctl renew <certificate-name> -n <namespace>
# Create CertificateRequest for testing
cmctl create certificaterequest my-cr \
--from-certificate-file cert.yaml \
--namespace default
# Approve/Deny CertificateRequest (if approval is required)
cmctl approve <certificaterequest-name> -n <namespace>
cmctl deny <certificaterequest-name> -n <namespace>
# Convert legacy cert-manager resources
cmctl convert -f old-resources.yamlBest Practices
Renewal Buffer Configuration
Configura ventanas de renovación adecuadas para prevenir la expiración de certificados:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-cert
spec:
# Certificate valid for 90 days
duration: 2160h
# Renew 30 days before expiry (gives time for issues)
renewBefore: 720h
# For short-lived certificates (1 hour)
# duration: 1h
# renewBefore: 30mBackup CA Strategy
Mantén siempre una copia de seguridad de la CA para recuperación ante desastres:
# Backup CA certificate and key
kubectl get secret ca-key-pair -n cert-manager -o yaml > ca-backup.yaml
# Store securely (encrypted, off-cluster)
# Consider using AWS Secrets Manager or HashiCorp Vault for CA storage
# Restore procedure
kubectl apply -f ca-backup.yamlMulti-Tenant Issuer Strategy
# ClusterIssuer for shared infrastructure
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: platform-team@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
---
# Namespace-scoped Issuer for team-specific CA
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: team-a-ca
namespace: team-a
spec:
ca:
secretName: team-a-ca-keypair
---
# RBAC for namespace issuers
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cert-manager-issuer-admin
namespace: team-a
rules:
- apiGroups: ["cert-manager.io"]
resources: ["issuers"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
- apiGroups: ["cert-manager.io"]
resources: ["certificates", "certificaterequests"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]RBAC Configuration
# Allow developers to create Certificates but not Issuers
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cert-manager-certificate-creator
rules:
- apiGroups: ["cert-manager.io"]
resources: ["certificates"]
verbs: ["create", "delete", "get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]
# Note: Don't grant create/delete on secrets unless needed
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: developers-certificate-creator
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cert-manager-certificate-creator
apiGroup: rbac.authorization.k8s.ioPrivate Key Rotation
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: rotating-cert
spec:
secretName: rotating-cert-tls
dnsNames:
- app.example.com
privateKey:
# Rotate key on each renewal
rotationPolicy: Always
algorithm: ECDSA
size: 256
issuerRef:
name: ca-issuer
kind: ClusterIssuerSummary and References
Key Concepts Summary
| Concept | Description |
|---|---|
| Certificate | Declares desired certificate, triggers issuance |
| Issuer | Namespace-scoped certificate authority configuration |
| ClusterIssuer | Cluster-wide certificate authority configuration |
| CertificateRequest | Represents a single certificate signing request |
| ACME | Protocol for automated certificate issuance (Let's Encrypt) |
| HTTP-01 | ACME challenge via HTTP endpoint verification |
| DNS-01 | ACME challenge via DNS TXT record verification |
| trust-manager | Distributes CA bundles across namespaces |
| istio-csr | Integrates cert-manager with Istio for workload certs |
Issuer Selection Guide
| Scenario | Recommended Issuer |
|---|---|
| Development/Testing | SelfSigned or CA |
| Public websites | ACME (Let's Encrypt) |
| Internal services with existing PKI | CA or Vault |
| AWS-native enterprise | AWS PCA |
| Multi-cloud enterprise | Vault PKI |
| Service mesh workloads | CA with istio-csr/Linkerd integration |
Official References
| Resource | URL |
|---|---|
| cert-manager Documentation | https://cert-manager.io/docs/ |
| cert-manager GitHub | https://github.com/cert-manager/cert-manager |
| ACME Protocol RFC | https://datatracker.ietf.org/doc/html/rfc8555 |
| Let's Encrypt Documentation | https://letsencrypt.org/docs/ |
| AWS PCA Issuer | https://github.com/cert-manager/aws-privateca-issuer |
| ACM Automated Certificate Management for Kubernetes (Dec 15, 2025) | https://aws.amazon.com/about-aws/whats-new/2025/12/acm-automated-certificate-management-kubernetes |
| istio-csr | https://github.com/cert-manager/istio-csr |
| trust-manager | https://github.com/cert-manager/trust-manager |
| cmctl CLI | https://cert-manager.io/docs/reference/cmctl/ |
| Helm Chart | https://artifacthub.io/packages/helm/cert-manager/cert-manager |
Version Compatibility Matrix
| cert-manager | Kubernetes | Helm |
|---|---|---|
| 1.16.x | 1.28 - 1.33 | 3.x |
| 1.15.x | 1.27 - 1.32 | 3.x |
| 1.14.x | 1.26 - 1.31 | 3.x |
| 1.13.x | 1.25 - 1.30 | 3.x |