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
- Visit the Cloudflare API Tokens page.
- Click "Create Token"
- Select the "Edit zone DNS" template
- Token name: External DNS
- Limit the token to your domain
- Click "Continue to summary", then "Create Token"
- 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.