cert-manager Reference: ClusterIssuer, Certificate, Ingress TLS & Vault PKI
cert-manager automates TLS certificate management in Kubernetes — requesting, renewing, and distributing certificates from Let’s Encrypt, Vault, and other issuers. This covers the resources and patterns you need in production.
1. Installation & Core Resources
Install cert-manager and understand the CRDs
# Install via Helm (recommended): helm repo add jetstack https://charts.jetstack.io helm repo update helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \ --set installCRDs=true # creates CRDs automatically # Or via kubectl (specific version — don't use :latest in prod): kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml # Verify installation: kubectl get pods -n cert-manager # controller + webhook + cainjector kubectl get crds | grep cert-manager.io # 6 CRDs should appear # Core resources: # Issuer — namespaced: issues certs within one namespace # ClusterIssuer — cluster-wide: issues certs across all namespaces # Certificate — request for a TLS cert (cert-manager creates it) # CertificateRequest — internal (auto-created by cert-manager) # Order / Challenge — Let's Encrypt ACME flow (auto-managed) # Secret — stores the issued TLS cert (auto-created by cert-manager) # Install cmctl (cert-manager CLI): brew install cmctl # macOS cmctl check api # verify cert-manager is working
2. ClusterIssuers — Let’s Encrypt
HTTP-01 and DNS-01 challenge configuration
# HTTP-01 challenge (default — needs port 80 accessible):
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com # IMPORTANT: use a real monitored email
privateKeySecretRef:
name: letsencrypt-prod-key # cert-manager creates this Secret
solvers:
- http01:
ingress:
class: nginx # must match your IngressClass name
# or: ingressClassName: nginx
# Staging issuer for testing (high rate limits — use this first!):
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-staging-key
solvers:
- http01:
ingress:
class: nginx
# DNS-01 challenge (for wildcard certs or private clusters with no public HTTP):
# Requires DNS provider API access (Route53, Cloudflare, GCP, Azure DNS, etc.)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-dns-key
solvers:
- dns01:
cloudflare:
email: admin@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
letsencrypt-staging first. Let’s Encrypt production has a rate limit of 5 duplicate certificates per week. Hitting it blocks all renewal for your domain for 7 days.3. Certificate Resource
Request certificates explicitly (without Ingress annotation)
# Explicit Certificate resource (most control):
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-app-tls
namespace: production
spec:
secretName: my-app-tls-secret # Secret cert-manager will create/update
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: my-app.example.com # main domain
dnsNames:
- my-app.example.com
- www.my-app.example.com # SAN (Subject Alternative Name)
- "*.my-app.example.com" # wildcard — requires DNS-01 challenge
duration: 2160h # 90 days (Let's Encrypt default)
renewBefore: 360h # renew 15 days before expiry
usages:
- server auth # TLS server certificate
- client auth # add if also used for mTLS
privateKey:
algorithm: ECDSA # faster than RSA, same security level
size: 256 # P-256
# Check cert status:
kubectl get certificate -n production # READY=True = cert issued + stored
kubectl describe certificate my-app-tls -n production # events + next renewal time
kubectl get secret my-app-tls-secret -n production -o yaml # tls.crt + tls.key
# Inspect the cert:
kubectl get secret my-app-tls-secret -n production \
-o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout | grep -A2 "Validity"
# Force renewal:
cmctl renew my-app-tls -n production
4. Ingress Annotation (auto-cert)
Let cert-manager create certs automatically from Ingress
# The annotation approach — cert-manager watches Ingresses and auto-creates Certificate resources:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: production
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod" # or issuer for namespaced
# cert-manager.io/common-name: "my-app.example.com" # optional, defaults to first host
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- my-app.example.com
secretName: my-app-tls-secret # cert-manager creates this Secret
rules:
- host: my-app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
# After applying:
kubectl get certificate -n production # cert-manager created Certificate automatically
kubectl describe ingress my-app -n production # check TLS section + events
# Annotation vs explicit Certificate:
# Annotation: simpler, less control (no duration/renewBefore/privateKey config)
# Explicit Certificate: full control, needed for wildcard certs + non-Ingress use cases (gRPC, etc.)
5. Vault Issuer
Issue internal certificates from HashiCorp Vault PKI
# Vault PKI setup (in Vault):
vault secrets enable pki
vault write pki/root/generate/internal \
common_name=example.com ttl=87600h
vault write pki/config/urls \
issuing_certificates="https://vault:8200/v1/pki/ca" \
crl_distribution_points="https://vault:8200/v1/pki/crl"
vault write pki/roles/cert-manager \
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
# ClusterIssuer pointing to Vault:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-issuer
spec:
vault:
server: https://vault.vault.svc.cluster.local:8200
path: pki/sign/cert-manager # Vault PKI role endpoint
auth:
kubernetes:
role: cert-manager
mountPath: /v1/auth/kubernetes
serviceAccountRef:
name: cert-manager # cert-manager's own service account
6. Troubleshooting
Debug certificate issuance failures
# Step 1: Check Certificate status kubectl get certificate -A # find non-Ready certs kubectl describe certificate-n # Look at: Status.Conditions, Events # Step 2: Check CertificateRequest kubectl get certificaterequest -n kubectl describe certificaterequest -n # Step 3: For ACME, check Order and Challenge kubectl get order -n kubectl describe order -n # shows challenge status kubectl get challenge -n kubectl describe challenge -n # HTTP-01: "Presented challenge using http-01 challenge mechanism" → check Ingress created # DNS-01: Check DNS propagation (may take 1-5 min) # Step 4: Check cert-manager controller logs kubectl logs -n cert-manager deploy/cert-manager --tail=50 kubectl logs -n cert-manager deploy/cert-manager-webhook --tail=20 # Common failures: # - CertificateRequest: "error: 429 urn:ietf:params:acme:error:rateLimited" # → Switch to staging issuer, hit rate limit. Wait 1 week or use staging. # - Challenge: "Error presenting challenge: port 80 not reachable" # → HTTP-01 needs public access on port 80. Check Ingress + firewall. # - Certificate stuck Pending: check cert-manager controller logs for DNS errors. # - Webhook timeout: kubectl get pods -n cert-manager → ensure all 3 pods Running. # Force re-issue (delete Certificate + cert-manager recreates): kubectl delete secret my-app-tls-secret -n production # cert-manager detects missing Secret and issues a new cert automatically
cmctl status certificate my-app-tls -n production for a single-command status summary including renewal time, issuer, and challenge status.
Track cert-manager, Kubernetes, and infrastructure releases.
ReleaseRun monitors Kubernetes, Docker, and 13+ DevOps technologies.
Related: Kubernetes YAML Reference | HashiCorp Vault Reference | Nginx Reference
🔍 Free tool: K8s YAML Security Linter — check your cert-manager Certificate and Issuer manifests for K8s security misconfigurations.
Founded
2023 in London, UK
Contact
hello@releaserun.com