Overview
cert-manager automates the management and issuance of TLS certificates in Kubernetes. It integrates with Let’s Encrypt and other certificate authorities to automatically provision, renew, and manage certificates for your applications.
Installation
Prerequisites
Install Helm (if not already installed):
# Download and install Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Verify installation
helm version
Install cert-manager
Install cert-manager using Helm:
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
# Update your local Helm chart repository cache
helm repo update
# Install cert-manager with CRDs
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.14.0 \
--set installCRDs= true
The --set installCRDs=true flag automatically installs the Custom Resource Definitions (CRDs) required by cert-manager.
Verify Installation
# Check that cert-manager pods are running
kubectl get pods -n cert-manager
# Expected output:
# NAME READY STATUS RESTARTS AGE
# cert-manager-* 1/1 Running 0 1m
# cert-manager-cainjector-* 1/1 Running 0 1m
# cert-manager-webhook-* 1/1 Running 0 1m
ClusterIssuer Configuration
A ClusterIssuer is a cluster-wide resource that represents a certificate authority from which signed certificates can be obtained.
Let’s Encrypt Production Issuer
Create a ClusterIssuer for Let’s Encrypt production:
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 expiration notifications
email : [email protected]
# Secret to store the ACME account private key
privateKeySecretRef :
name : letsencrypt-prod
# ACME challenge solver configuration
solvers :
- http01 :
ingress :
class : nginx
Apply the ClusterIssuer:
kubectl apply -f issuer.yml
Let’s Encrypt Staging Issuer (Testing)
For testing, use the staging environment to avoid rate limits:
apiVersion : cert-manager.io/v1
kind : ClusterIssuer
metadata :
name : letsencrypt-staging
spec :
acme :
# Let's Encrypt staging server (higher rate limits)
server : https://acme-staging-v02.api.letsencrypt.org/directory
email : [email protected]
privateKeySecretRef :
name : letsencrypt-staging
solvers :
- http01 :
ingress :
class : nginx
Always test with the staging issuer first! Let’s Encrypt production has strict rate limits: 50 certificates per registered domain per week.
ACME Challenge Methods
HTTP-01 Challenge (Recommended for Most Cases)
Proves domain ownership by serving a specific file over HTTP:
solvers :
- http01 :
ingress :
class : nginx
Requirements:
Port 80 must be accessible from the internet
Domain must resolve to your cluster’s ingress controller
Pros: Simple, works with most setups
Cons: Requires port 80 access, doesn’t support wildcard certificates
DNS-01 Challenge (For Wildcard Certificates)
Proves domain ownership by creating a DNS TXT record:
solvers :
- dns01 :
cloudDNS :
project : my-gcp-project
serviceAccountSecretRef :
name : clouddns-dns01-solver-sa
key : key.json
Pros: Supports wildcard certificates, doesn’t require public ingress
Cons: Requires DNS provider API access, slower validation
Certificate Management
Automatic Certificate with Ingress
The simplest method - cert-manager automatically creates and manages certificates:
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : ingress-nginx
annotations :
# Tell cert-manager to issue a certificate
cert-manager.io/cluster-issuer : letsencrypt-prod
spec :
ingressClassName : nginx
tls :
- hosts :
- exchange.jogeshwar.xyz
# Secret where certificate will be stored
secretName : exchange-tls
rules :
- host : exchange.jogeshwar.xyz
http :
paths :
- path : /
pathType : Prefix
backend :
service :
name : exchange-service
port :
number : 80
When you apply this Ingress:
cert-manager detects the cert-manager.io/cluster-issuer annotation
Automatically creates a Certificate resource
Initiates ACME challenge with Let’s Encrypt
Stores the issued certificate in the exchange-tls Secret
NGINX Ingress uses the certificate for TLS termination
This is the recommended approach! You don’t need to manually create Certificate resources.
Manual Certificate Resource (Advanced)
For more control, explicitly create a Certificate resource:
apiVersion : cert-manager.io/v1
kind : Certificate
metadata :
name : exchange-cert
namespace : default
spec :
# Secret where certificate will be stored
secretName : exchange-tls
# Certificate duration before renewal
duration : 2160h # 90 days
renewBefore : 360h # 15 days
# Subject information
subject :
organizations :
- My Company
# Certificate properties
isCA : false
privateKey :
algorithm : RSA
encoding : PKCS1
size : 2048
# Valid usages
usages :
- server auth
- client auth
# Domain names
dnsNames :
- exchange.jogeshwar.xyz
- www.exchange.jogeshwar.xyz
# Reference to ClusterIssuer
issuerRef :
name : letsencrypt-prod
kind : ClusterIssuer
group : cert-manager.io
With the Ingress annotation method, you typically don’t need separate Certificate resources. cert-manager creates them automatically.
Verification and Management
Check ClusterIssuer Status
# List all cluster issuers
kubectl get clusterissuer
# Describe issuer for detailed status
kubectl describe clusterissuer letsencrypt-prod
Look for Ready: True in the status.
Check Certificate Status
# List certificates in a namespace
kubectl get certificate -n default
# Describe certificate for details
kubectl describe certificate exchange-cert
# Check certificate ready condition
kubectl get certificate exchange-cert -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}'
Inspect Certificate Secret
# View the certificate secret
kubectl get secret exchange-tls -n default -o yaml
# Decode and inspect the certificate
kubectl get secret exchange-tls -n default -o jsonpath='{.data.tls\.crt}' | \
base64 --decode | \
openssl x509 -text -noout
Check Certificate Requests
# List certificate requests (tracks issuance process)
kubectl get certificaterequest -n default
# View certificate request details
kubectl describe certificaterequest < request-nam e >
Check ACME Challenges
# List active challenges
kubectl get challenge -n default
# Describe challenge to see validation status
kubectl describe challenge < challenge-nam e >
Certificate Renewal
cert-manager automatically renews certificates before they expire:
Default renewal : 30 days before expiration (for 90-day Let’s Encrypt certs)
Configurable via renewBefore field in Certificate spec
# Force certificate renewal
kubectl delete secret exchange-tls
# cert-manager will automatically recreate it
# Or use cert-manager kubectl plugin
kubectl cert-manager renew exchange-cert
Troubleshooting
Certificate Not Issuing
Check cert-manager logs:
kubectl logs -n cert-manager -l app=cert-manager
Check certificate status:
kubectl describe certificate exchange-cert
Check certificate request:
kubectl describe certificaterequest
HTTP-01 Challenge Failing
# Check challenge status
kubectl describe challenge
# Common issues:
# - Domain doesn't resolve to your cluster
# - Port 80 not accessible
# - Ingress class mismatch
# - Firewall blocking traffic
# Test domain resolution
nslookup exchange.jogeshwar.xyz
# Test HTTP access
curl http://exchange.jogeshwar.xyz/.well-known/acme-challenge/test
Rate Limit Errors
If you hit Let’s Encrypt rate limits:
Switch to staging issuer for testing
Wait for the rate limit window to reset
Check current limits: https://letsencrypt.org/docs/rate-limits/
Delete Old Certificates
If you need to clean up and start fresh:
# Delete the certificate resource
kubectl delete certificate exchange-cert
# Delete the corresponding secret
kubectl delete secret exchange-tls
# cert-manager will recreate them based on Ingress annotations
Best Practices
Use Ingress Annotations Let cert-manager manage certificates automatically via Ingress annotations rather than manual Certificate resources
Test with Staging Always test certificate issuance with Let’s Encrypt staging before switching to production
Monitor Expiration Set up alerts for certificate expiration even with auto-renewal
Backup Secrets Back up certificate Secrets as part of your disaster recovery plan
Multiple Domains
Single Certificate, Multiple Domains
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : multi-domain-ingress
annotations :
cert-manager.io/cluster-issuer : letsencrypt-prod
spec :
ingressClassName : nginx
tls :
- hosts :
- api.example.com
- www.example.com
- admin.example.com
secretName : multi-domain-tls
rules :
- host : api.example.com
http :
paths :
- path : /
pathType : Prefix
backend :
service :
name : api-service
port :
number : 80
- host : www.example.com
http :
paths :
- path : /
pathType : Prefix
backend :
service :
name : web-service
port :
number : 80
Separate Certificates per Domain
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : multi-cert-ingress
annotations :
cert-manager.io/cluster-issuer : letsencrypt-prod
spec :
ingressClassName : nginx
tls :
- hosts :
- api.example.com
secretName : api-tls
- hosts :
- www.example.com
secretName : www-tls
rules :
- host : api.example.com
http :
paths :
- path : /
pathType : Prefix
backend :
service :
name : api-service
port :
number : 80
- host : www.example.com
http :
paths :
- path : /
pathType : Prefix
backend :
service :
name : web-service
port :
number : 80
Additional Resources