Skip to main content

cert-manager

Purpose: For platform engineers, shows how to configure cert-manager Issuers, ClusterIssuers, and certificate policies.

What cert-manager Does

cert-manager automates TLS certificate provisioning and renewal insiypt (ACME), internal CAs, and Vault. Platform services like Gateway API, Harbor, and Keycloak depend on cert-manager for their TLS certificates.

How It's Deployed

cert-manager is deployed via FluxCD from openCenter-gitops-base:

openCenter-gitops-base/applications/base/services/cert-manager/
├── namespace.yaml
├── source.yaml # HelmRepository for jetstack charts
├── helmrelease.yaml
└── helm-values/
└── hardened-values-v1.18.2.yaml

The customer overles cluster-specific overrides:

applications/overlays/<cluster>/services/cert-manager/
├── kustomization.yaml
└── override-values.yaml

FluxCD reconciles the base HelmRelease merged with override values, deploying cert-manager into the cert-manager namespace.

Key Configuration

ClusterIssuer for Let's Encrypt

Most clusters use a ClusterIssuer for ACME (Let's Encrypt) certificate issuance:

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

For DNS-01 challenges (wildcard certificates), configure a DNS provider solver instead of HTTP-01.

Internal CA Issuer

For clusters that use an internal certificate authority:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
internal-ca
spec:
ca:
secretName: internal-ca-keypair

Create the CA secret (encrypt with SOPS before committing):

kubectl create secret tls internal-ca-keypair \
--cert=ca.crt --key=ca.key -n cert-manager

Requesting a Certificate

Services request certificates by creating Certificate resources:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-tls
namespace: my-app
spec:
secretName: app-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- app.example.com
duration: 2160h # 90 days
renewBefore: 360h # Renew 15 days before expiry

Verification

# Check cert-manager pods
kubectl get pods -n cert-manager

# Verify ClusterIssuers are ready
kubectl get clusterissuers

# Check certificate status
kubectl get certificates -A

# Inspect a specific certificate's events
kubectl describe certificate app-tls -n my-app

# Check for failed certificate requests
kubectl get certificaterequests -A

Troubleshooting

Certificate stuck in "Issuing": Check CertificateRequest and Order resources for errors. Common causes: DNS not resolving, HTTP-01 challenge endpoint unreachable, ACME rate limits.

kubectl get orders -A
kubectl describe order <order-name> -n <namespace>

Common Customizations

  • Switch ACME solver: Change from HTTP-01 to DNS-01 for wildcard certificates or clusters wilic ingress.
  • Add trust bundles: Use trust-manager to distribute CA bundles across namespaces.
  • Adjust renewal window: Set renewBefore on Certificate resources to control when renewal starts.
  • Resource limits: Increase memory in override-values.yaml for clusters with many certificates.