Skip to content

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

  1. Overview
  2. Architecture
  3. Installation
  4. Core Concepts
  5. Issuer Types
  6. EKS Integration Patterns
  7. AWS-Native Alternative: ACM + ACK
  8. Service Mesh Integration
  9. trust-manager
  10. Monitoring and Troubleshooting
  11. Best Practices
  12. Summary and References

Overview

What cert-manager Solves

La gestión manual de certificados en entornos Kubernetes presenta desafíos operativos importantes:

ChallengeImpactcert-manager Solution
Manual renewalService outages from expired certificatesAutomatic renewal before expiry
Inconsistent processesSecurity gaps and configuration driftDeclarative Certificate resources
Key managementRisk of key exposureAutomatic key generation and rotation
Multi-issuer complexityOperational overheadUnified interface for all CA types
GitOps incompatibilityCannot version control secretsCertificate 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

ComponentResponsibilityKey Functions
ControllerMain reconciliation loopWatches Certificate CRs, creates CertificateRequests, stores issued certs
WebhookAdmission controlValidates and mutates cert-manager resources
cainjectorCA bundle injectionInjects 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+
  • kubectl configurado con acceso de administrador del cluster
  • Helm 3.x (para el método de instalación con Helm)
bash
# 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=30

Production Helm Values

yaml
# 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
bash
# Install with custom values
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.16.2 \
  -f cert-manager-values.yaml

Installation with kubectl

bash
# 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-manager

Verify Installation

bash
# 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 api

Core Concepts

Custom Resource Definitions (CRDs)

cert-manager introduce varios CRDs para gestionar el ciclo de vida de los certificados:

CRDScopePurpose
CertificateNamespacedDeclares desired certificate properties
CertificateRequestNamespacedRepresents a CSR bound to an issuer
IssuerNamespacedDefines how to obtain certificates (namespace-scoped)
ClusterIssuerClusterDefines how to obtain certificates (cluster-wide)
OrderNamespacedRepresents an ACME order
ChallengeNamespacedRepresents an ACME challenge

Certificate Resource

El recurso Certificate es la interfaz principal para solicitar certificados:

yaml
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.io

Issuer vs ClusterIssuer

yaml
# 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: nginx

CertificateRequest Resource

CertificateRequests suelen ser creados automáticamente por cert-manager:

yaml
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 auth

Issuer Types

SelfSigned Issuer (Development/Testing)

Los certificados autofirmados son útiles para entornos de desarrollo y pruebas:

yaml
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.io

CA Issuer (Internal PKI)

Para organizaciones con su propia Certificate Authority interna:

yaml
# 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: ClusterIssuer

ACME / 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

yaml
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: nginx

DNS-01 Solver with Route53 and IRSA

yaml
# 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: ClusterIssuer

AWS Private CA Issuer

Para entornos empresariales que requieren AWS Private Certificate Authority:

bash
# 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
yaml
# 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.io

HashiCorp Vault PKI

Para organizaciones que usan HashiCorp Vault como backend de PKI:

yaml
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=1h

EKS Integration Patterns

TLS Termination Comparison

ApproachTLS TerminationCertificate SourceUse Case
ALB + ACMAt ALBAWS Certificate ManagerPublic-facing with AWS-managed certs
ALB + cert-managerAt ALBcert-managerPublic-facing with custom CA
NLB + IngressAt Ingress Controllercert-managerLayer 4 load balancing
NLB + PodAt Podcert-managerEnd-to-end encryption
Gateway APIAt Gatewaycert-managerModern 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

yaml
# 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: 80

NLB with TLS Termination at Ingress Controller

yaml
# 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: ClusterIssuer

Gateway API Integration

yaml
# 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: 80

AWS-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

TypeUse Case
ACM Exportable Public CertificatesPublic-domain certificates exported to a Kubernetes Secret for direct use by Pods/Ingress
AWS Private CAInternal 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

yaml
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: ack

El 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

Aspectcert-managerACM + ACK
IssuersLet's Encrypt, Vault, AWS PCA, and moreACM (public), AWS Private CA
EcosystemCNCF open source, vendor-neutralAWS-native, IAM-based access control
What you installcert-manager controllerACK service controller for ACM
CostFree (infrastructure cost only)Standard ACM/AWS Private CA pricing; no additional charge for the Kubernetes integration itself
Best fitMulti-cloud, or ACME issuers requiredAWS-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

bash
# 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.pem

Configuring Istio to use istio-csr

yaml
# 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-configmap

Linkerd Trust Anchor Management

Linkerd requiere un certificado de trust anchor para mTLS. cert-manager puede gestionarlo:

yaml
# 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
bash
# 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

bash
helm repo add jetstack https://charts.jetstack.io
helm install trust-manager jetstack/trust-manager \
  --namespace cert-manager \
  --set app.trust.namespace=cert-manager

Bundle Resource

yaml
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: enabled

Using Trust Bundles in Applications

yaml
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-bundle

Monitoring and Troubleshooting

Prometheus Metrics

cert-manager expone métricas para monitorizar la salud de los certificados:

yaml
# 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: 30s

Key Metrics

MetricDescriptionAlert Threshold
certmanager_certificate_ready_statusCertificate ready state (1=ready, 0=not ready)!= 1
certmanager_certificate_expiration_timestamp_secondsCertificate expiry timestamp< 7 days
certmanager_certificate_renewal_timestamp_secondsNext renewal timestampPast due
certmanager_controller_sync_call_countController sync operationsSpike detection
certmanager_http_acme_client_request_countACME HTTP requestsRate limiting detection

PrometheusRule for Alerting

yaml
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

bash
# 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 -noout

Common Errors and Solutions

ErrorCauseSolution
Waiting for HTTP-01 challenge propagationChallenge endpoint not accessibleCheck Ingress, Service, firewall rules
DNS problem: NXDOMAINDNS record not createdVerify Route53 permissions, hosted zone ID
Error presenting challenge: 403 ForbiddenIRSA/IAM permissions issueCheck ServiceAccount annotations, IAM policy
acme: error code 429Rate limit exceededWait 1 hour, use staging server for testing
certificate is not valid for any namesDNS name mismatchVerify dnsNames in Certificate spec
Error getting keypair for CA issuerCA secret missing or malformedCheck CA secret exists with correct keys

cmctl CLI Tool

bash
# 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.yaml

Best Practices

Renewal Buffer Configuration

Configura ventanas de renovación adecuadas para prevenir la expiración de certificados:

yaml
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: 30m

Backup CA Strategy

Mantén siempre una copia de seguridad de la CA para recuperación ante desastres:

bash
# 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.yaml

Multi-Tenant Issuer Strategy

yaml
# 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

yaml
# 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.io

Private Key Rotation

yaml
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: ClusterIssuer

Summary and References

Key Concepts Summary

ConceptDescription
CertificateDeclares desired certificate, triggers issuance
IssuerNamespace-scoped certificate authority configuration
ClusterIssuerCluster-wide certificate authority configuration
CertificateRequestRepresents a single certificate signing request
ACMEProtocol for automated certificate issuance (Let's Encrypt)
HTTP-01ACME challenge via HTTP endpoint verification
DNS-01ACME challenge via DNS TXT record verification
trust-managerDistributes CA bundles across namespaces
istio-csrIntegrates cert-manager with Istio for workload certs

Issuer Selection Guide

ScenarioRecommended Issuer
Development/TestingSelfSigned or CA
Public websitesACME (Let's Encrypt)
Internal services with existing PKICA or Vault
AWS-native enterpriseAWS PCA
Multi-cloud enterpriseVault PKI
Service mesh workloadsCA with istio-csr/Linkerd integration

Official References

ResourceURL
cert-manager Documentationhttps://cert-manager.io/docs/
cert-manager GitHubhttps://github.com/cert-manager/cert-manager
ACME Protocol RFChttps://datatracker.ietf.org/doc/html/rfc8555
Let's Encrypt Documentationhttps://letsencrypt.org/docs/
AWS PCA Issuerhttps://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-csrhttps://github.com/cert-manager/istio-csr
trust-managerhttps://github.com/cert-manager/trust-manager
cmctl CLIhttps://cert-manager.io/docs/reference/cmctl/
Helm Charthttps://artifacthub.io/packages/helm/cert-manager/cert-manager

Version Compatibility Matrix

cert-managerKubernetesHelm
1.16.x1.28 - 1.333.x
1.15.x1.27 - 1.323.x
1.14.x1.26 - 1.313.x
1.13.x1.25 - 1.303.x