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.

  1. Visit the Cloudflare API Tokens page.
  2. Click "Create Token"
  3. Select the "Edit zone DNS" template
  4. Token name: Cert Manager
  5. Limit the token to your domain
  6. Click "Continue to summary", then "Create Token"
  7. 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.