External-DNS automates one of the most error-prone parts of Kubernetes operations: keeping DNS records synchronized with your cluster's services and ingresses. When you create a Service of type LoadBalancer or an Ingress resource with a hostname, External-DNS reads it and creates or updates the corresponding DNS records in your configured provider — Route53, Cloudflare, Azure DNS, Google Cloud DNS, and many others.
The problem is that External-DNS is easy to break and easy to ignore. When it stops working, no new services get DNS entries, but existing services continue working fine. Teams don't notice until they deploy a new service and discover DNS records never appeared — often after hours of debugging application configuration that was never the real issue.
In this tutorial you'll set up monitoring for External-DNS using Vigilmon to catch controller failures and sync problems as soon as they occur.
Why External-DNS needs external monitoring
External-DNS failures are insidious because they don't affect running services. Only new services, ingresses, and record updates are broken. Here are the failure modes that teams regularly miss:
- Controller crashes — the External-DNS pod fails and restarts or enters CrashLoopBackOff. All DNS automation stops. No records are created or updated until the pod recovers.
- Provider API connectivity lost — the controller is running but can't reach your DNS provider's API (Route53, Cloudflare, etc.) due to a network policy change, expired credentials, or an API rate limit. The controller keeps running but no records are synced.
- Credentials expired — the IAM role, service account, or API token used by External-DNS expires. The controller logs errors but stays running. DNS records become stale as services change.
- Reconciliation loop stalls — the controller's reconciliation loop stops making progress, perhaps due to a large batch of records, a deadlock in provider interaction, or a memory issue. Records diverge from the desired state silently.
- Webhook validation failure — if you run External-DNS with a webhook provider, a failure in the webhook receiver breaks all DNS operations.
The common thread: External-DNS can be completely broken while appearing healthy from a kubectl get pods perspective.
What you'll need
- A Kubernetes cluster running External-DNS (v0.13+ recommended)
- External-DNS configured with a supported DNS provider
- Access to the health/metrics port (default
7979) - A free Vigilmon account
Step 1: Locate the External-DNS health endpoint
External-DNS exposes an HTTP health endpoint and Prometheus metrics. The default port is 7979:
# Find the External-DNS deployment
kubectl get deployment -A | grep external-dns
# Check what port it's using
kubectl describe deployment external-dns -n external-dns | grep -A5 "Ports"
Test the health endpoint from inside the cluster:
# Port-forward to test
kubectl port-forward -n external-dns deployment/external-dns 7979:7979 &
# Test the health endpoint
curl http://localhost:7979/healthz
# Response: OK
# Check metrics
curl http://localhost:7979/metrics | grep external_dns
kill %1
The /healthz endpoint returns OK when the controller is running. The /metrics endpoint exposes reconciliation counters that indicate sync health.
Step 2: Expose the health endpoint for external monitoring
To monitor External-DNS with Vigilmon, you need to make the health endpoint reachable from outside the cluster. The cleanest approach is a NodePort service:
# external-dns-health-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: external-dns-health
namespace: external-dns
spec:
type: NodePort
selector:
app.kubernetes.io/name: external-dns
ports:
- name: health
port: 7979
targetPort: 7979
nodePort: 30979
protocol: TCP
kubectl apply -f external-dns-health-nodeport.yaml
Verify it works from outside the cluster:
curl http://<any-node-ip>:30979/healthz
# Response: OK
Step 3: Set up controller health monitoring in Vigilmon
Add the health check as a Vigilmon monitor:
- Log in to vigilmon.online and go to Monitors → New Monitor
- Choose HTTP / HTTPS
- URL:
http://<node-ip>:30979/healthz - Check interval: 1 minute
- Under Expected response:
- Status code:
200 - Response body contains:
OK
- Status code:
- Save as "External-DNS Controller Health"
This confirms the controller process is alive. A failed check means the pod has crashed, been evicted, or entered a state where it can't serve health checks.
Step 4: Monitor DNS record sync status
The health endpoint tells you the controller is running, but not whether it's successfully syncing records to your DNS provider. To monitor sync health, expose the metrics endpoint:
# external-dns-metrics-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: external-dns-metrics
namespace: external-dns
spec:
type: NodePort
selector:
app.kubernetes.io/name: external-dns
ports:
- name: metrics
port: 7979
targetPort: 7979
nodePort: 30979
protocol: TCP
The key metrics for sync health are:
# Successful sync operations
external_dns_registry_records{registry="..."} <count>
# Source records read from Kubernetes
external_dns_source_endpoints_total <count>
# Errors in sync operations
external_dns_errors_total <count>
For Vigilmon monitoring, use the metrics endpoint as a health signal — if it stops responding, the controller is unhealthy. Add a second monitor:
- Go to Monitors → New Monitor
- Choose HTTP / HTTPS
- URL:
http://<node-ip>:30979/metrics - Check interval: 2 minutes
- Expected status code:
200 - Response body contains:
external_dns - Save as "External-DNS Metrics Endpoint"
Step 5: Monitor provider API connectivity
External-DNS must reach your DNS provider's API to create records. The most reliable way to monitor this connection from outside the cluster is to check that records you expect to exist actually resolve correctly in DNS.
For example, if External-DNS should create a record api.yourdomain.com pointing to your ingress IP:
# Verify the record resolves correctly
dig +short api.yourdomain.com
# Expected: <your-ingress-ip>
Set up a Vigilmon HTTP monitor for each critical DNS-managed endpoint:
- Go to Monitors → New Monitor
- Choose HTTP / HTTPS
- URL:
https://api.yourdomain.com/health - Check interval: 1 minute
- Expected status code:
200 - Save as "DNS Sync Check — api.yourdomain.com"
If External-DNS stops syncing and an ingress IP changes, this monitor will catch the mismatch — the old DNS record will point to a defunct IP or the new IP's health check will fail with a cert mismatch.
Step 6: Monitor the reconciliation loop
External-DNS runs a reconciliation loop on a configured interval (default 1 minute with --interval=1m). If the loop stalls, records stop being updated even if the controller appears healthy.
Create a test service specifically for monitoring reconciliation — an endpoint that changes predictably so you can verify External-DNS is actively syncing:
# external-dns-canary-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: dns-health-canary
namespace: external-dns
annotations:
external-dns.alpha.kubernetes.io/hostname: dns-canary.yourdomain.com
external-dns.alpha.kubernetes.io/ttl: "30"
spec:
type: LoadBalancer
selector:
app: dns-health-canary
ports:
- port: 80
targetPort: 8080
Then monitor the canary DNS name with Vigilmon:
- Go to Monitors → New Monitor
- Choose HTTP / HTTPS
- URL:
http://dns-canary.yourdomain.com - Check interval: 5 minutes
- Save as "External-DNS Reconciliation Canary"
If this monitor fails, External-DNS has stopped writing records to your DNS provider.
Step 7: Configure alert channels
External-DNS failures have a delayed blast radius — they break new deployments rather than existing ones. Configure alerts so the right people are notified without triggering P1 pagers for every transient failure:
- Go to Alert Channels → Add Channel → Email
- Add your platform engineering team email
- Go to Alert Channels → Add Channel → Webhook
- Add your Slack webhook URL for
#platform-alerts
For the controller health monitor: route to Slack and email. A crashed controller should be fixed within an hour before it blocks deployments.
For the metrics endpoint: route to Slack only. The metrics endpoint going down temporarily is less urgent than the health endpoint.
For DNS sync canary: route to email and Slack. A failed canary means DNS is actively broken for new deployments.
Set confirmation thresholds:
- Controller health:
2 consecutive failures(accounts for pod restarts) - Metrics endpoint:
3 consecutive failures - DNS sync canary:
2 consecutive failures
Correlating Vigilmon alerts with External-DNS state
When an External-DNS alert fires:
# 1. Check pod status and restarts
kubectl get pods -n external-dns -o wide
# 2. Check recent logs — look for provider errors
kubectl logs -n external-dns deployment/external-dns --since=10m | \
grep -E "error|Error|failed|Failed"
# 3. Check for credential or API errors
kubectl logs -n external-dns deployment/external-dns --since=1h | \
grep -iE "unauthorized|forbidden|rate limit|throttl"
# 4. Check sync events
kubectl get events -n external-dns --sort-by=.lastTimestamp | tail -20
# 5. Verify the DNS provider credentials secret
kubectl get secret -n external-dns
kubectl describe secret <credentials-secret> -n external-dns
Provider-specific checks:
# For Route53: verify IAM role or credentials
aws sts get-caller-identity
# For Cloudflare: test the API token
curl -H "Authorization: Bearer <token>" \
"https://api.cloudflare.com/client/v4/user/tokens/verify"
# For Azure DNS: check service principal
az account show
Monitoring summary
| Monitor | Type | Interval | What it catches |
|---|---|---|---|
| External-DNS Controller Health | HTTP | 60s | Controller crash, pod eviction |
| External-DNS Metrics Endpoint | HTTP | 2min | Process health, metrics availability |
| DNS Sync Canary | HTTP | 5min | Reconciliation loop stall, provider API failure |
| api.yourdomain.com | HTTP | 60s | Stale records after IP change |
What's next
- SSL certificate monitoring — External-DNS often pairs with cert-manager. Vigilmon monitors your certificate expiry and alerts you before a rotation failure leaves users with TLS errors.
- Multi-provider monitoring — if you use External-DNS with multiple providers in split configurations, create canary records for each provider and monitor them independently.
- Status page — expose a Vigilmon status page for your cluster's DNS health so application teams can check if External-DNS is working without needing cluster access.
Start monitoring External-DNS for free at vigilmon.online — catch DNS sync failures before they block your next deployment.