Enable HTTPS with Let’s Encrypt DNS-01 Challenge in Traefik on Kubernetes

🔄 This post builds on my previous guide:

Self-Managed Kubernetes Cluster on AWS (with Traefik & MetalLB)

If you haven’t set up your Kubernetes cluster, MetalLB, and Traefik yet, start there first!

In this follow-up, we’ll extend that setup by enabling automatic HTTPS using Let’s Encrypt with the DNS-01 challenge in Traefik. This method is ideal for:

  • Environments behind firewalls or NAT (no need to expose port 80/443)
  • Wildcard certificates like *.maksonlee.com
  • Clusters managed in self-hosted infrastructure

This guide uses Cloudflare as the DNS provider.

âś… Prerequisites

  • You’ve deployed Traefik via Helm
  • MetalLB is exposing Traefik with a static IP (e.g., 10.0.128.240)
  • Your domain (e.g., maksonlee.com) is managed by Cloudflare
  • You created A or CNAME records in Cloudflare for your app (e.g., whoami.maksonlee.com)
  • Those DNS records must be DNS-only (Cloudflare proxy disabled)
  1. Create a Cloudflare API Token
  • Go to https://dash.cloudflare.com/profile/api-tokens
  • Click “Create Token”
  • Choose “Edit zone DNS” template
  • Scope the token to your zone (e.g., maksonlee.com)
  • Copy the token — you’ll use it in the next step
  1. Store the API Token as a Kubernetes Secret

Create a secret in the same namespace as Traefik (e.g., kube-system):

kubectl create secret generic cloudflare-api-secret --namespace kube-system --from-literal=CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-token

Replace your-cloudflare-token with your real token string.

  1. Create a Persistent Volume for acme.json

Let’s Encrypt needs to persist certificate data in acme.json.

Create a PVC for that:

# acme-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: acme-pvc
  namespace: kube-system
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: ebs-sc
  resources:
    requests:
      storage: 1Gi

Apply it:

kubectl apply -f acme-pvc.yaml
  1. Update traefik-values.yaml for Cloudflare DNS-01

Edit the values used to deploy Traefik via Helm:

service:
  type: LoadBalancer
  loadBalancerIP: 10.0.128.240

additionalArguments:
  - "--ping=true"
  - "--ping.entrypoint=traefik"
  - "--api.dashboard=true"
  - "--certificatesresolvers.dns.acme.dnschallenge=true"
  - "--certificatesresolvers.dns.acme.dnschallenge.provider=cloudflare"
  - "--certificatesresolvers.dns.acme.email=your-email@example.com"
  - "--certificatesresolvers.dns.acme.storage=/data/acme.json"
  - "--certificatesresolvers.dns.acme.dnschallenge.resolvers=1.1.1.1:53"

envFrom:
  - secretRef:
      name: cloudflare-api-secret

ingressClass:
  enabled: true
  isDefaultClass: true
  name: traefik

deployment:
  additionalVolumes:
    - name: acme-storage
      persistentVolumeClaim:
        claimName: acme-pvc

  additionalVolumeMounts:
    - name: acme-storage
      mountPath: /data

nodeSelector:
  kubernetes.io/hostname: ip-10-0-128-6

tolerations:
  - key: "node-role.kubernetes.io/control-plane"
    operator: "Exists"
    effect: "NoSchedule"

âś… Be sure to:

  • Replace your-email@example.com with your real email.
  • Replace ip-10-0-128-6 with your control-plane node hostname.
  1. Upgrade Traefik with New Settings

Apply the updated Helm values:

helm upgrade traefik traefik/traefik --namespace kube-system -f traefik-values.yaml
  1. Add or Update Your Ingress for TLS

Update the Ingress to request a certificate via Let’s Encrypt DNS-01:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
    traefik.ingress.kubernetes.io/router.tls.certresolver: dns
spec:
  rules:
    - host: whoami.maksonlee.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: whoami
                port:
                  number: 80

Apply it:

kubectl apply -f whoami.yaml
  1. Confirm Certificate Issuance

Check Traefik logs:

kubectl logs -n kube-system deploy/traefik

Traefik’s log should include:

Register... providerName=dns.acme
Testing certificate renew... acmeCA=https://acme-v02.api.letsencrypt.org/directory providerName=dns.acme

These lines confirm:

  • ACME provider is registered (dns.acme)
  • Traefik is checking and renewing certs properly

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top