Skip to content

How to Set Up ExternalDNS in Kubernetes

DodaTech Updated 2026-06-24 3 min read

In this tutorial, you'll learn about How to Set Up ExternalDNS in Kubernetes. We cover key concepts, practical examples, and best practices.

The Problem

You installed ExternalDNS, created an Ingress or Service with LoadBalancer type, but no DNS record is created. Your domain remains unreachable despite the LoadBalancer having a public IP. ExternalDNS is not synchronizing Kubernetes resources to your DNS provider.

Quick Fix

Fix 1: Annotations Missing on Ingress/Service

WRONG — creating an Ingress without ExternalDNS annotations:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  # missing external-dns annotation
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app
            port: 80

RIGHT — add the annotation:

metadata:
  annotations:
    external-dns.alpha.kubernetes.io/hostname: app.example.com

Fix 2: Wrong DNS Provider Configuration

WRONG — ExternalDNS configured for Cloudflare but using AWS Route53:

# Deployment args:
args:
- --provider=aws
# (but the domain is hosted on Cloudflare)

RIGHT — match the provider to your DNS hosting:

args:
- --provider=cloudflare
env:
- name: CF_API_TOKEN
  valueFrom:
    secretKeyRef:
      name: cloudflare-token
      key: api-token

Common providers: aws, google, <a href="/web-servers-hosting/cloudflare/">cloudflare</a>, azure, digitalocean, rfc2136.

Fix 3: Zone Not Found

kubectl logs deployment/external-dns
# time="2024-03-15T10:30:00Z" level=error msg="No hosted zones found"

RIGHT — specify the domain filter:

args:
- --domain-filter=example.com
- --zone-id-filter=ZONE123456  # optional, for specific zone

Check that the DNS zone exists in the provider:

# For AWS:
aws route53 list-hosted-zones --query "HostedZones[?Name=='example.com.']"
# For Cloudflare:
curl -H "Authorization: Bearer $CF_TOKEN" https://api.cloudflare.com/client/v4/zones?name=example.com

Fix 4: Source Type Not Specified

WRONG — ExternalDNS does not know which resources to watch:

args:
- --source=service
# (but you are using Ingress — Ingress sources are not watched)

RIGHT — specify all relevant sources:

args:
- --source=service
- --source=ingress
- --source=crd  # for custom resources like Ambassador HTTP

Fix 5: Ownership Not Working

# ExternalDNS creates records but also deletes records it should not

RIGHT — use ownership with TXT records:

args:
- --txt-owner-id=my-cluster
- --txt-prefix=external-dns-

Fix 6: Private DNS or VPC

For private hosted zones, set:

args:
- --aws-zone-type=private  # for AWS private zones

Fix 7: Dry Run Mode

ExternalDNS runs in dry-run mode by default if you forgot to remove the flag:

args:
- --dry-run  # remove this flag to make actual changes

Check the logs for what changes would be made:

kubectl logs deployment/external-dns | grep "CREATE\|UPDATE\|DELETE"

Use DodaTech's DNS Inspector to verify ExternalDNS records, compare desired vs actual DNS state, and troubleshoot synchronization delays.

Prevention

  • Add external-dns.alpha.kubernetes.io/hostname annotation to all Ingress and LoadBalancer resources.
  • Verify the DNS provider credentials are correct.
  • Use --domain-filter to limit the scope.
  • Start with --dry-run to verify changes before applying.
  • Monitor ExternalDNS logs for errors.

Common Mistakes with external dns

  1. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  2. Using return to exit a function early instead of wrapping a pure value in the monad
  3. Mixing let bindings with <- bindings in do notation, producing type errors

These mistakes appear frequently in real-world K8S code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### How long does ExternalDNS take to create records?

ExternalDNS synchronizes every 60 seconds by default (controlled by --interval=60s). DNS propagation to global resolvers takes additional time (seconds to minutes depending on TTL).

Why does ExternalDNS create TXT records alongside A records?

TXT records track ownership — ExternalDNS uses them to know which records it created and to avoid deleting records owned by other instances. The TXT record contains the --txt-owner-id value.

Can ExternalDNS update existing DNS records?

Yes, ExternalDNS overwrites existing records that match the domain filter. It does not touch records outside its managed zones. Use --managed-record-types=A,AAAA,CNAME to specify which record types to manage.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro