How to Install and Configure cert-manager with Cloudflare on Kubernetes
When deploying modern apps to Kubernetes, HTTPS isn’t optional. Manually managing TLS certificates is tedious and prone to errors. This is where cert-manager
comes in. It's a Kubernetes-native certificate management controller that automates the lifecycle of SSL certificates.
In this tutorial, we’ll walk through how to install and configure cert-manager
to automatically issue and renew HTTPS certificates using Let’s Encrypt, backed by Cloudflare DNS for DNS-01 challenge validation.
We'll assume:
- You already have a Kubernetes cluster up and running; if you don't, you can create one on Civo.
- Your domain is hosted in Cloudflare
- You want fully automated certificate management using DNS-based validation
Step 1: Install cert-manager using Helm
First, add the Jetstack Helm repository:
helm repo add jetstack https://charts.jetstack.io
helm repo update
Now install cert-manager
and its required CRDs (Custom Resource Definitions):
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=true
This creates the necessary components in the cert-manager
namespace.
You can confirm the install:
kubectl get pods --namespace cert-manager
Step 2: Install the Ingress Nginx Controller
Add the ingress-nginx repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
Install the controller into its own namespace, ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace
Check that is created
kubectl get pods -n ingress-nginx
Step 3: Set Up Cloudflare API Token
To allow cert-manager
to create DNS records for Let’s Encrypt DNS-01 challenges, you need a Cloudflare API token with restricted permissions.
- Visit the Cloudflare API Tokens page.
- Click "Create Token"
- Select the "Edit zone DNS" template
- Token name: Cert Manager
- Limit the token to your domain
- Click "Continue to summary", then "Create Token"
- Copy the token and store it somewhere safe
Step 4: Create a Kubernetes Secret for the Token
Create a secret in the cert-manager
namespace with your Cloudflare token.
kubectl create secret generic cloudflare-api-token-secret \
--from-literal=api-token=<YOUR_CLOUDFLARE_TOKEN> \
--namespace cert-manager
Replace <YOUR_CLOUDFLARE_TOKEN>
with the actual token (don’t check this into version control!).
Step 5: Create the ClusterIssuer
Create a ClusterIssuer
resource to define Let’s Encrypt as the certificate authority using Cloudflare for DNS-01 challenges.
# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns
spec:
acme:
email: you@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-dns-key
solvers:
- dns01:
cloudflare:
email: you@example.com
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
Change the you@example.com
email addresses to your email.
Apply it:
kubectl apply -f cluster-issuer.yaml
Step 6: Issue a Certificate
Now create a Certificate
resource to request a certificate for your app.
# example-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-cert
namespace: default
spec:
secretName: example-markcallen-tls
issuerRef:
name: letsencrypt-dns
kind: ClusterIssuer
dnsNames:
- example.markcallen.com
Make sure you replace example.markcallen.com
with your domain name.
Apply the certificate request:
kubectl apply -f example-cert.yaml
cert-manager will handle the rest:
- Create a DNS TXT record via Cloudflare
- Complete the ACME challenge with Let’s Encrypt
- Store the certificate and key in a Kubernetes secret
You can monitor the progress with:
kubectl describe certificate example-cert
kubectl describe order
Step 7: Create a Deployment and Service
Need something to show
# demployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nginx-example
namespace: default
labels:
app: nginx-example
spec:
replicas: 1
selector:
matchLabels:
app: nginx-example
template:
metadata:
labels:
app: nginx-example
spec:
containers:
- name: nginx
image: "nginx"
---
apiVersion: v1
kind: Service
metadata:
name: example-service
namespace: default
spec:
selector:
app: nginx-example
ports:
- name: http
targetPort: 80
port: 80
Make sure that it is running
kubectl get pods
kubectl get svc
Step 8: Use the Certificate in an Ingress
Assuming you’re using an Ingress controller (like NGINX), reference the certificate secret like this:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-dns
spec:
ingressClassName: nginx
tls:
- hosts:
- example.markcallen.com
secretName: example-markcallen-tls
rules:
- host: example.markcallen.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
Apply the Ingress and wait a minute or two for the certificate to be issued and mounted:
kubectl apply -f ingress.yaml
See that you have a DNS entry for the ingress controller
kubectl get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
example-ingress nginx example.markcallen.com 212.2.241.106 80, 443 101s
Test out using curl
and the address from above.
curl https://example.markcallen.com --resolve 'example.markcallen.com:443:212.2.241.106'
I've created a repo here with the manifests: https://github.com/markcallen/cert-manager-example
Final Thoughts
By leveraging cert-manager
and Cloudflare DNS, you now have a secure, automated certificate lifecycle baked into your Kubernetes platform. No more cron jobs. No more copy-pasting PEM files. Just HTTPS that works forever.