Automate Kubernetes DNS with ExternalDNS and Cloudflare

Manually managing DNS records every time you spin up a new Kubernetes Ingress resource is a tedious task, especially in fast-paced environments. That’s where ExternalDNS comes in. This tool automates the creation and management of DNS records in providers like Cloudflare, using Kubernetes-native resources like Ingress.

In this guide, we’ll walk through how to use ExternalDNS with Cloudflare to manage DNS for your Ingress controller. Once set up, your cluster will automatically create DNS records for any annotated Ingress resource.


☁️ Prerequisites

Before you start, make sure you have the following:

  • A Kubernetes cluster (EKS, GKE, Civo, kind, etc.)
  • A domain managed in Cloudflare (e.g. example.markcallen.com)
  • An Ingress controller installed (ingress-nginx, Traefik, etc.)
  • Helm installed locally

🔐 Step 1: Create a Cloudflare API Token

  1. Visit the Cloudflare API Tokens page.
  2. Click "Create Token"
  3. Select the "Edit zone DNS" template
  4. Token name: External DNS
  5. Limit the token to your domain
  6. Click "Continue to summary", then "Create Token"
  7. Copy the token and store it somewhere safe

This token allows ExternalDNS to manage DNS records on your behalf. Be sure to treat it like a password.


🔑 Step 2: Create Namesapce and a Kubernetes Secret for the Token

Save the token as a secret in the external-dns namespace:

kubectl create ns external-dns
kubectl create secret generic cloudflare-api-token \
--from-literal=apiKey='YOUR_CLOUDFLARE_API_TOKEN' \
-n external-dns

Replace 'YOUR_CLOUDFLARE_API_TOKEN' with your actual token string.


🚀 Step 3: Install ExternalDNS via Helm

Add the Helm repo:

helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm repo update

Create a values.yaml

provider:
  name: cloudflare
  domainFilters:
    - markcallen.com
  policy: sync
  txtOwnerId: k8s
  sources:
    - ingress
env:
  - name: CF_API_TOKEN
    valueFrom:
      secretKeyRef:
        name: cloudflare-api-token
        key: apiKey

Then install ExternalDNS:

helm upgrade --install external-dns external-dns/external-dns \
--namespace external-dns \
--values values.yaml

Check to make sure it's running:

kubectl --namespace=external-dns get pods \
-l "app.kubernetes.io/name=external-dns,app.kubernetes.io/instance=external-dns"

Step 4: 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 4: Annotate Your Ingress Resources

To trigger ExternalDNS, annotate your Ingress resources with the desired DNS hostname:

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  namespace: default
  annotations:
    external-dns.alpha.kubernetes.io/hostname: example.markcallen.com
spec:
  ingressClassName: nginx
  rules:
  - host: example.markcallen.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: example-service
            port:
              number: 80

Apply the Ingress resource and ExternalDNS will automatically create a corresponding DNS record in Cloudflare.


🔎 Step 5: Validate DNS Propagation

Check that the record was created:

kubectl logs -n external-dns deploy/external-dns

And test DNS resolution:

dig example.markcallen.com +short

You should see the IP or hostname of your Ingress controller.


Created a github repo with this manifests: https://github.com/markcallen/external-dns-example

🎯 Wrapping Up

ExternalDNS turns DNS management into a declarative, Kubernetes-native process. By linking your cluster to Cloudflare, every annotated Ingress becomes a hands-free DNS update. If you're running dynamic workloads or deploying frequently, this setup will save you time and reduce manual errors.