Automating SSL Certificates in Kubernetes with cert-manager
If you’ve ever managed SSL certificates in a Kubernetes environment, you probably know it can be a real headache. Manual certificate renewals, Secret configuration, synchronization with Ingress… Fortunately, there’s cert-manager - a tool that automates this entire process and lets you finally sleep peacefully.
What Is cert-manager?
cert-manager is a native Kubernetes controller that automates the management and issuance of TLS certificates from various sources. It works as a Kubernetes API extension, adding Custom Resources that allow you to define certificates declaratively.
Key Advantages
Full Automation
- Automatic certificate issuance
- Automatic renewal before expiration
- Integration with Kubernetes Ingress
- Zero manual intervention
Support for Multiple Sources
- Let’s Encrypt (ACME)
- HashiCorp Vault
- Venafi
- Private CA (self-signed)
- External issuers (custom integrations)
Kubernetes-Native
- Declarative configuration
- CRDs (Custom Resource Definitions)
- kubectl integration
- GitOps ready
Installing cert-manager
Prerequisites
Before installation, make sure you have:
- Kubernetes cluster (version 1.22+)
- kubectl configured
- Cluster admin rights
- Helm 3.x (optional, but recommended)
Installation via Helm
The simplest way is to use Helm:
# Add Helm repository
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Install CRDs
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.crds.yaml
# Install cert-manager
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.13.3
# Verify installation
kubectl get pods -n cert-manager
Installation via kubectl
If you prefer pure YAML:
# Install cert-manager with all components
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml
# Check status
kubectl get pods -n cert-manager
kubectl get crds | grep cert-manager
After installation, you should see three pods:
- cert-manager
- cert-manager-cainjector
- cert-manager-webhook
Configuring Let’s Encrypt
ClusterIssuer vs Issuer
cert-manager offers two types of resources for issuing certificates:
Issuer - works within a single namespace ClusterIssuer - works cluster-wide (recommended)
Configuring ClusterIssuer for Let’s Encrypt
Let’s start with the staging environment (for testing):
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
# Let's Encrypt staging server (higher limits, test certificates)
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected] # Your email for notifications
# Secret to store ACME private key
privateKeySecretRef:
name: letsencrypt-staging
# HTTP-01 challenge
solvers:
- http01:
ingress:
class: nginx # or traefik, depending on Ingress Controller
For production:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
Apply the configuration:
kubectl apply -f clusterissuer-staging.yaml
kubectl apply -f clusterissuer-prod.yaml
# Check status
kubectl get clusterissuer
kubectl describe clusterissuer letsencrypt-prod
Automatic Certificates for Ingress
Basic Ingress Configuration
The simplest way is to add annotations to Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# Optional: HTTP -> HTTPS redirect
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls # cert-manager will create this Secret automatically
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
After applying:
kubectl apply -f ingress.yaml
# Monitor certificate issuance process
kubectl get certificate
kubectl describe certificate myapp-tls
# Check events
kubectl get events --sort-by='.lastTimestamp'
Manual Certificate Management
You can also create a Certificate resource directly:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-cert
namespace: default
spec:
secretName: myapp-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: myapp.example.com
dnsNames:
- myapp.example.com
- www.myapp.example.com
# Automatic renewal 30 days before expiration
renewBefore: 720h # 30 days
DNS-01 Challenge for Wildcard Certificates
For wildcard certificates (*.example.com) you must use DNS-01 challenge:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-dns
solvers:
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
Create Secret with Cloudflare API token:
kubectl create secret generic cloudflare-api-token \
--from-literal=api-token='your-cloudflare-api-token'
Certificate for wildcard:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-cert
spec:
secretName: wildcard-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- "example.com"
- "*.example.com"
Monitoring and Debugging
Checking Certificate Status
# List all certificates
kubectl get certificate --all-namespaces
# Certificate details
kubectl describe certificate myapp-tls
# CertificateRequest status
kubectl get certificaterequest
kubectl describe certificaterequest <name>
# Order status (ACME)
kubectl get order
kubectl describe order <name>
# Challenge status
kubectl get challenge
kubectl describe challenge <name>
cert-manager Logs
# Main controller
kubectl logs -n cert-manager deploy/cert-manager
# Webhook
kubectl logs -n cert-manager deploy/cert-manager-webhook
# CA injector
kubectl logs -n cert-manager deploy/cert-manager-cainjector
# Follow logs in real-time
kubectl logs -n cert-manager deploy/cert-manager -f
Common Issues
Issue 1: Certificate in “Pending” state
# Check CertificateRequest
kubectl get certificaterequest
kubectl describe certificaterequest <name>
# Check Order
kubectl get order
kubectl describe order <name>
Common causes:
- Incorrect DNS configuration
- Domain not accessible by Let’s Encrypt
- Ingress Controller issues
Issue 2: HTTP-01 challenge fails
# Check if challenge pod was created
kubectl get pods | grep cm-acme-http-solver
# Check if Ingress for challenge was created
kubectl get ingress
Solution:
- Check if domain points to cluster
- Check if port 80 is open
- Check Ingress Controller logs
Issue 3: Too many requests (rate limiting)
Let’s Encrypt has limits:
- 50 certificates per domain weekly
- 5 duplicates weekly
Solution:
- Use staging during testing
- Plan production deployments
Integration with Monitoring
Prometheus Metrics
cert-manager exports metrics:
apiVersion: v1
kind: Service
metadata:
name: cert-manager-metrics
namespace: cert-manager
labels:
app: cert-manager
spec:
ports:
- name: metrics
port: 9402
targetPort: 9402
selector:
app: cert-manager
ServiceMonitor for Prometheus Operator:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: cert-manager
namespace: cert-manager
spec:
selector:
matchLabels:
app: cert-manager
endpoints:
- port: metrics
interval: 30s
Grafana Dashboard
Import dashboard with ID: 11001 (cert-manager overview)
Key metrics:
certmanager_certificate_expiration_timestamp_seconds- expiration datecertmanager_certificate_ready_status- readiness statuscertmanager_http_acme_client_request_count- ACME requestscertmanager_controller_sync_call_count- sync calls
Alerting
Example Prometheus alert:
groups:
- name: cert-manager
rules:
- alert: CertificateExpiryIn7Days
expr: (certmanager_certificate_expiration_timestamp_seconds - time()) / 86400 < 7
for: 1h
labels:
severity: warning
annotations:
summary: "Certificate {{ $labels.name }} expires in less than 7 days"
- alert: CertificateNotReady
expr: certmanager_certificate_ready_status == 0
for: 10m
labels:
severity: critical
annotations:
summary: "Certificate {{ $labels.name }} is not ready"
Advanced Configurations
Private CA (Private Certificate Authority)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: private-ca
spec:
ca:
secretName: ca-key-pair
Creating CA:
# Generate CA key and certificate
openssl genrsa -out ca.key 4096
openssl req -new -x509 -key ca.key -out ca.crt -days 3650
# Create Secret
kubectl create secret tls ca-key-pair \
--cert=ca.crt \
--key=ca.key \
--namespace=cert-manager
HashiCorp Vault Integration
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vault-issuer
spec:
vault:
server: https://vault.example.com
path: pki/sign/example-dot-com
auth:
kubernetes:
mountPath: /v1/auth/kubernetes
role: cert-manager
secretRef:
name: cert-manager-vault-token
key: token
Certificate Policies
Enforcing organizational standards:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-cert
spec:
secretName: myapp-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- myapp.example.com
# Minimum key length
privateKey:
algorithm: RSA
size: 4096
# Additional extensions
usages:
- digital signature
- key encipherment
- server auth
- client auth
Best Practices
1. Use Namespace for cert-manager
# Everything in dedicated namespace
kubectl create namespace cert-manager
2. Resource Limits
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
3. Backup Secrets
# Backup certificates
kubectl get secrets -n default -o yaml > certificates-backup.yaml
# Or use Velero for automatic backups
4. Staging before Production
Always test on Let’s Encrypt staging before production.
5. Monitoring and Alerting
Monitor:
- Expiration dates
- Certificate status
- Renewal errors
6. Documentation
Maintain documentation:
- List of used issuers
- Manual renewal procedures (in case of failure)
- Contacts and escalation
Migration from Existing Certificates
Import Existing Certificate
# Create Secret with existing certificate
kubectl create secret tls existing-cert \
--cert=path/to/cert.crt \
--key=path/to/cert.key
# Create Certificate managed by cert-manager
kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: existing-cert
spec:
secretName: existing-cert
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- example.com
EOF
Gradual Migration
- Install cert-manager alongside existing solution
- Configure ClusterIssuers
- Test on staging environment
- Migrate individual applications
- Monitor and validate
- Remove old solution
Case Study: Migrating 50+ Applications
Challenge: Company had 50+ applications in Kubernetes, each with manually managed certificates.
Solution:
- cert-manager installation
- ClusterIssuers configuration (prod + staging)
- Migration script for all Ingress
- Automatic annotation addition
- Grafana monitoring
Results:
- 95% reduction in certificate management time
- 0 incidents with expired certificates in a year
- Unified approach to SSL/TLS
- Full renewal automation
Migration script code:
#!/bin/bash
# migrate-ingresses.sh
for ingress in $(kubectl get ingress -A -o json | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"'); do
namespace=$(echo $ingress | cut -d/ -f1)
name=$(echo $ingress | cut -d/ -f2)
echo "Migrating $namespace/$name"
kubectl annotate ingress $name -n $namespace \
cert-manager.io/cluster-issuer=letsencrypt-prod \
--overwrite
echo "Done: $namespace/$name"
done
Summary
cert-manager is a must-have for every Kubernetes cluster handling HTTPS traffic. Certificate management automation:
- Eliminates risk of expired certificates
- Reduces ops work by 90%+
- Provides consistent approach to SSL/TLS
- Integrates with Kubernetes ecosystem
Quick Start - Checklist
- Install cert-manager via Helm
- Configure ClusterIssuer for Let’s Encrypt staging
- Test on sample application
- Configure production ClusterIssuer
- Add annotations to Ingress
- Configure monitoring (Grafana + Prometheus)
- Set up alerts for expiring certificates
- Document configuration
Next Steps
- Explore external issuers for custom CAs
- Integrate with Vault for sensitive environments
- Automate certificate backups
- Consider certificate policies for compliance
Want to make sure all your certificates are monitored? Try CrtMgr - a simple tool for tracking SSL/TLS certificates with automatic alerts that works perfectly with cert-manager.
Automation is key. With cert-manager and proper monitoring, you’ll never lose sleep over expired certificates again.