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.
HelmRelease Configuration
cert-manager is deployed using Flux with Gateway API support enabled:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: flux-system
spec:
chart:
spec:
chart: cert-manager
reconcileStrategy: ChartVersion
sourceRef:
kind: HelmRepository
name: cert-manager
namespace: cert-manager
version: 1.18.2
interval: 1m0s
releaseName: cert-manager
targetNamespace: cert-manager
install:
crds: Create
upgrade:
crds: CreateReplace
values:
config:
apiVersion: controller.config.cert-manager.io/v1alpha1
kind: ControllerConfiguration
enableGatewayAPI: true
crds:
enabled: true
Configuration Parameters
cert-manager Helm chart version. Currently using 1.18.2
spec.values.config.enableGatewayAPI
Enable Gateway API support for HTTP-01 challenges
Automatically install cert-manager CRDs
spec.targetNamespace
string
default:"cert-manager"
Namespace where cert-manager will be installed
ClusterIssuer Configuration
A ClusterIssuer defines how cert-manager will request certificates. Here’s the Let’s Encrypt production issuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cluster-issuer
spec:
acme:
email: [email protected]
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: issuer-account-key
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: http-gateway
namespace: kube-system
kind: Gateway
sectionName: http
Issuer Types
ClusterIssuer is cluster-scoped and can issue certificates for any namespace: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-key
solvers:
- http01:
ingress:
class: nginx
Use case: Centralized certificate management for the entire cluster Issuer is namespace-scoped and can only issue certificates in its namespace:apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-prod
namespace: my-app
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
Use case: Isolated certificate management per namespace
The cluster uses Gateway API HTTP-01 challenges instead of Ingress-based challenges for ACME validation.
Certificate Management
Automatic Certificate with Gateway
The easiest way to get a certificate is using Gateway annotations:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: https-gateway
annotations:
cert-manager.io/cluster-issuer: cluster-issuer
spec:
gatewayClassName: kgateway
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: app.example.com
tls:
mode: Terminate
certificateRefs:
- name: app-example-com-tls
Add annotation
Add the cert-manager.io/cluster-issuer annotation to your Gateway
Define TLS listener
Configure the HTTPS listener with hostname and certificate reference
Automatic certificate
cert-manager automatically creates and manages the certificate
Manual Certificate Resource
For more control, create a Certificate resource directly:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: app-tls
namespace: default
spec:
secretName: app-example-com-tls
issuerRef:
name: cluster-issuer
kind: ClusterIssuer
dnsNames:
- app.example.com
- www.app.example.com
duration: 2160h # 90 days
renewBefore: 720h # 30 days before expiry
Certificate Parameters
Name of the Secret where the certificate will be stored
Reference to the Issuer or ClusterIssuer
List of DNS names (SANs) for the certificate
Requested certificate lifetime (90 days)
When to renew the certificate before expiry (30 days)
Challenge Types
HTTP-01 Challenge
DNS-01 Challenge
HTTP-01 challenges validate domain ownership by serving a file over HTTP:solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: http-gateway
namespace: kube-system
kind: Gateway
sectionName: http
Advantages:
- Simple to configure
- Works with Gateway API or Ingress
- No DNS provider integration needed
Requirements:
- Domain must be publicly accessible on port 80
- Gateway/Ingress must route to cert-manager’s challenge solver
DNS-01 challenges validate domain ownership via DNS TXT records:solvers:
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
Advantages:
- Works for wildcard certificates
- No need for public HTTP access
- Can validate internal domains
Requirements:
- DNS provider API access
- Credentials stored in Kubernetes Secret
Wildcard Certificates
For wildcard certificates, use DNS-01 challenge:
wildcard-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-example-com
namespace: default
spec:
secretName: wildcard-example-com-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- "*.example.com"
- example.com
HTTP-01 challenges cannot be used for wildcard certificates. You must use DNS-01 challenges.
Verifying Certificate Status
Check certificate status
kubectl get certificates -A
Expected output:
NAMESPACE NAME READY SECRET AGE
default app-tls True app-example-com-tls 5d
default api-tls True api-example-com-tls 5d
View certificate details
kubectl describe certificate app-tls
Check certificate secret
kubectl get secret app-example-com-tls -o yaml
Inspect certificate content
kubectl get secret app-example-com-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout
Troubleshooting
Certificate stuck in pending
Check certificate status
kubectl describe certificate app-tls
Look for error messages in the Events sectionCheck CertificateRequest
kubectl get certificaterequest
kubectl describe certificaterequest <name>
Check Order and Challenge
kubectl get orders
kubectl get challenges
kubectl describe challenge <name>
View cert-manager logs
kubectl logs -n cert-manager -l app.kubernetes.io/name=cert-manager
HTTP-01 challenge failing
-
Verify Gateway is configured correctly:
kubectl get gateway http-gateway -n kube-system
-
Check that port 80 is accessible:
curl -I http://app.example.com/.well-known/acme-challenge/test
-
Verify HTTP-01 solver pod is running:
kubectl get pods -l acme.cert-manager.io/http01-solver=true
Staging Environment
For testing, use Let’s Encrypt staging environment:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-staging-key
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- name: http-gateway
namespace: kube-system
Always test with the staging environment first to avoid hitting Let’s Encrypt rate limits (5 certificates per domain per week in production).
Best Practices
- Use ClusterIssuer for cluster-wide certificate management
- Enable Gateway API support in cert-manager configuration
- Set appropriate
renewBefore duration (30 days recommended)
- Use staging issuer for testing new configurations
- Monitor certificate expiry dates
- Store ACME account keys securely
- Use DNS-01 challenges for wildcard certificates
Resources