k8s-dns-troubleshooting
Skill19 starsChanged 10 months agoMITView on GitHub
Use when pods cannot resolve service names, CoreDNS is unhealthy, DNS lookups are slow or timing out, service discovery is broken, or when performing a DNS health check on a Kubernetes cluster
In this repository19 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- cert-manager-troubleshootingSkill
- external-dns-troubleshootingSkill
- external-secrets-troubleshootingSkill
- flux-gitops-patternsSkill
- flux-operationsSkill
- flux-troubleshootingSkill
- helm-chart-developmentSkill
- helm-chart-maintenanceSkill
- helm-chart-reviewSkill
- helm-chart-testingSkill
- k8s-continual-improvementSkill
- k8s-namespace-troubleshootingSkill
- k8s-network-troubleshootingSkill
- k8s-platform-operationsSkill
- k8s-platform-tenancySkill
- k8s-security-hardeningSkill
- k8s-security-redteamSkill
- kyverno-troubleshootingSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. About 0 are common, 8 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 59
Run this checklist first for a fast pass/fail indicator of cluster DNS health.
- Distinctive: line 83
Test DNS from an existing running pod.
- Very specific: line 100
If nslookup/getent are not available in the pod image, rely on indirect indicators: Steps 1-2 passing (CoreDNS pods running, service and …
- Distinctive: line 102
If resolution fails, see Section 2 for comprehensive testing.
- Distinctive: line 131
When the health check reports DEGRADED or UNHEALTHY, use this decision tree to identify root causes.
- Distinctive: line 201
Test DNS using existing running pods — no new workloads are deployed.
- Very specific: line 213
Not all container images include DNS tools.
- Very specific: line 256
When kubectl exec is unavailable or pod images lack tools, verify DNS through infrastructure checks:
- Very specific: line 415
Use trailing dots in application config (FQDN): api.example.com.
- Distinctive: line 431
If CoreDNS is running and configured correctly but pods still cannot resolve, the network path may be blocked.
- Distinctive: line 451
If a namespace has an egress NetworkPolicy, it blocks all egress traffic by default — including DNS to kube-system.
- Very specific: line 492
If verbose logging is needed, recommend the user add log inside the Corefile server block (CoreDNS reloads automatically if the reload …
- Distinctive: line 563
When the appropriate MCP servers are connected, prefer these over raw kubectl where available:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Kubernetes DNS Troubleshooting
Investigate and diagnose internal Kubernetes DNS (CoreDNS). Perform a quick health assessment to determine whether cluster DNS is functioning correctly, then deep-dive into root causes when issues are found.
Keywords
coredns, kube-dns, dns, resolve, nslookup, dig, service discovery, NXDOMAIN, dns timeout, dns lookup, ndots, search domain, cluster.local, Corefile, dns policy, dns resolution, dns health, dns check, name resolution, dns-default, cluster-first
When to Use This Skill
- Pods cannot resolve service names (NXDOMAIN or timeouts)
- CoreDNS pods are crashing, restarting, or not running
- DNS lookups are slow (high latency on service calls)
- Service discovery is broken after a cluster upgrade or config change
- A general DNS health check is needed to confirm the cluster is healthy
- Applications log connection errors that trace back to DNS failures
- Cross-namespace service resolution is failing
- Headless service DNS records are not returning pod IPs
When NOT to Use
- DNS records for external providers are not syncing → use external-dns-troubleshooting
- TLS certificate issuance failures → use cert-manager-troubleshooting
- Service exists but has no endpoints → use k8s-namespace-troubleshooting
Note: Pods failing to resolve external domains (e.g., api.example.com) are in scope — this is an upstream forwarding issue diagnosed in Section 3 (Corefile) and Section 5 (network connectivity). ExternalDNS is a separate concern about publishing records to providers.
Related Skills
- external-dns-troubleshooting - External DNS record management
- k8s-namespace-troubleshooting - General namespace diagnosis
- k8s-platform-operations - Cluster-wide health checks
- k8s-network-troubleshooting - Network connectivity and service mesh issues
- k8s-security-hardening - Network policies that may block DNS
- Shared: Network Policies
Quick Reference
| Task | Command |
| CoreDNS pod status | kubectl get pods -n kube-system -l k8s-app=kube-dns |
| CoreDNS logs | kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100 |
| DNS service ClusterIP | kubectl get svc kube-dns -n kube-system |
| DNS endpoints | kubectl get endpoints kube-dns -n kube-system |
| Test DNS from existing pod | kubectl exec -n ${NS} ${POD} -- nslookup kubernetes.default |
| Find a running pod to exec into | kubectl get pods -A --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.namespace} {.items[0].metadata.name}' |
| View Corefile | kubectl get configmap coredns -n kube-system -o yaml |
| Pod DNS config | kubectl exec ${POD} -n ${NS} -- cat /etc/resolv.conf |
Quick DNS Health Check
Run this checklist first for a fast pass/fail indicator of cluster DNS health. Complete all checks before drawing conclusions.
Step 1: CoreDNS Pods
# Are CoreDNS pods running?
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
# Check for restarts (indicates instability)
kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range .status.containerStatuses[*]}{.restartCount}{end}{"\n"}{end}'Step 2: DNS Service and Endpoints
# kube-dns Service must exist with a ClusterIP
kubectl get svc kube-dns -n kube-system
# Endpoints must point to CoreDNS pod IPs
kubectl get endpoints kube-dns -n kube-systemStep 3: Resolution Test
Test DNS from an existing running pod. No new workloads are deployed.
# Find a running pod to use as a test subject
kubectl get pods -A --field-selector=status.phase=Running -o jsonpath='{range .items[0]}{.metadata.namespace}{" "}{.metadata.name}{"\n"}{end}'
# Verify the pod's resolv.conf points to the kube-dns ClusterIP
kubectl exec -n ${NS} ${POD} -- cat /etc/resolv.conf
# Test resolution (nslookup available in most images)
kubectl exec -n ${NS} ${POD} -- nslookup kubernetes.default.svc.cluster.local
# If nslookup is not available, try wget or getent as fallbacks
kubectl exec -n ${NS} ${POD} -- getent hosts kubernetes.default.svc.cluster.local
kubectl exec -n ${NS} ${POD} -- wget -qO- --timeout=5 http://kubernetes.default.svc:443 2>&1 | head -1If nslookup/getent are not available in the pod image, rely on indirect indicators: Steps 1-2 passing (CoreDNS pods running, service and endpoints healthy) plus no DNS errors in CoreDNS logs (Section 6) is strong evidence DNS is functional.
If resolution fails, see Section 2 for comprehensive testing. If it passes, see Section 2 for deeper checks (cross-namespace, headless, external resolution).
Health Summary Template
Present results using this format:
## DNS Health Summary
| Check | Status | Detail |
|-------|--------|--------|
| CoreDNS pods running | PASS/FAIL | X/Y pods ready, Z restarts |
| kube-dns Service exists | PASS/FAIL | ClusterIP: X.X.X.X |
| kube-dns Endpoints populated | PASS/FAIL | X endpoints |
| Resolve kubernetes.default (exec) | PASS/FAIL/SKIP | Resolved to X.X.X.X / Timeout / NXDOMAIN / No tools available |
| CoreDNS health endpoint | PASS/FAIL | localhost:8080/health returns OK |
| CoreDNS restart count | PASS/WARN | Total restarts across pods |
**Overall: HEALTHY / DEGRADED / UNHEALTHY**- HEALTHY — All checks pass, zero restarts
- DEGRADED — Resolution works but CoreDNS has restarts or reduced replicas
- UNHEALTHY — Resolution fails or CoreDNS pods are down
Diagnostic Workflow
When the health check reports DEGRADED or UNHEALTHY, use this decision tree to identify root causes.
DNS resolution failing?
├─ CoreDNS pods not running?
│ ├─ Pods Pending → Scheduling issue (Section 1)
│ ├─ Pods CrashLoopBackOff → Bad Corefile or OOM (Section 1, Section 3)
│ └─ Deployment missing → CoreDNS not installed or deleted (Section 1)
├─ CoreDNS pods running but resolution fails?
│ ├─ kube-dns Service missing ClusterIP → Service deleted (Section 1)
│ ├─ kube-dns Endpoints empty → Label mismatch or pods not ready (Section 1)
│ ├─ Pod /etc/resolv.conf wrong → dnsPolicy misconfigured (Section 4)
│ ├─ Internal names fail, external works → Corefile zone config (Section 3)
│ ├─ External names fail, internal works → Upstream forwarder issue (Section 3)
│ ├─ All lookups timeout → Network path blocked (Section 5)
│ └─ Intermittent failures → Overloaded CoreDNS or conntrack (Section 1, Section 5)
├─ DNS slow but eventually resolves?
│ ├─ High ndots causing extra queries → ndots config (Section 4)
│ ├─ CoreDNS CPU/memory saturated → Resource limits (Section 1)
│ └─ Upstream DNS slow → Forwarder latency (Section 3)
└─ Only some pods affected?
├─ Check pod dnsPolicy → Per-pod DNS config (Section 4)
├─ Network policy blocking egress → UDP/TCP 53 blocked (Section 5)
└─ Node-specific issue → kube-proxy or iptables (Section 5)Section 1: CoreDNS Component Health
# Deployment status
kubectl get deploy coredns -n kube-system
kubectl describe deploy coredns -n kube-system
# Pod details — check status, restarts, node placement
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
# Resource consumption (requires metrics-server)
kubectl top pods -n kube-system -l k8s-app=kube-dns
# Check for OOMKills
kubectl get pods -n kube-system -l k8s-app=kube-dns -o json | \
jq -r '.items[] | .metadata.name as $pod | .status.containerStatuses[]? | select(.lastState.terminated.reason == "OOMKilled") | "\($pod)\tOOMKilled"'
# Resource requests and limits
kubectl get deploy coredns -n kube-system -o jsonpath='{.spec.template.spec.containers[0].resources}' | jq .
# Events related to CoreDNS
kubectl get events -n kube-system --field-selector involvedObject.name=coredns --sort-by='.lastTimestamp'
# Service and endpoints
kubectl get svc kube-dns -n kube-system -o yaml
kubectl get endpoints kube-dns -n kube-system -o yamlWhat to Look For
| Symptom | Likely Cause | Diagnostic |
| 0/2 pods ready | CoreDNS crashed or not scheduled | Check events and logs |
| High restart count | OOMKill or Corefile error | Check terminated reason and resource limits |
| Endpoints empty | Pods not ready or label mismatch | Verify pod labels match k8s-app=kube-dns |
| CPU near limits | High query volume | Check if CPU is near limits; recommend increasing CPU limits or adding replicas |
| Memory near limits | Large cache or zone data | Check if memory is near limits; recommend increasing memory limits |
Section 2: DNS Resolution Testing
Test DNS using existing running pods — no new workloads are deployed. Pick a pod that is already running in the cluster.
Finding a Test Pod
# List running pods (pick one with a shell — alpine, debian, ubuntu images work well)
kubectl get pods -A --field-selector=status.phase=Running -o wide
# Check what tools are available in a pod
kubectl exec -n ${NS} ${POD} -- sh -c 'which nslookup dig getent wget curl 2>/dev/null'Not all container images include DNS tools. Try commands in this order of preference: nslookup, getent hosts, wget/curl (connection attempt reveals DNS). If no tools are available, use indirect methods (resolv.conf inspection + CoreDNS log analysis).
Basic Resolution Tests
# Resolve the Kubernetes API service (must always work)
kubectl exec -n ${NS} ${POD} -- nslookup kubernetes.default.svc.cluster.local
# Resolve with fully qualified name vs short name
kubectl exec -n ${NS} ${POD} -- sh -c \
'echo "--- FQDN ---" && nslookup kubernetes.default.svc.cluster.local && echo "--- Short ---" && nslookup kubernetes.default'
# Resolve a service in a specific namespace
kubectl exec -n ${NS} ${POD} -- nslookup ${SERVICE}.${TARGET_NS}.svc.cluster.local
# Test external resolution (upstream forwarding)
kubectl exec -n ${NS} ${POD} -- nslookup www.google.comFallback: getent or wget
# If nslookup is not available, use getent
kubectl exec -n ${NS} ${POD} -- getent hosts kubernetes.default.svc.cluster.local
kubectl exec -n ${NS} ${POD} -- getent hosts ${SERVICE}.${TARGET_NS}.svc.cluster.local
# If neither is available, a wget/curl connection attempt tests DNS indirectly
# (DNS failure shows as "bad address" or "could not resolve host")
kubectl exec -n ${NS} ${POD} -- wget -qO- --timeout=3 http://${SERVICE}.${TARGET_NS}.svc.cluster.local 2>&1 | head -3Targeted Queries Against CoreDNS Directly
# Get kube-dns ClusterIP
DNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
# Query CoreDNS directly by specifying the nameserver (bypasses pod resolv.conf)
kubectl exec -n ${NS} ${POD} -- nslookup kubernetes.default.svc.cluster.local ${DNS_IP}Indirect DNS Verification (No Exec Required)
When kubectl exec is unavailable or pod images lack tools, verify DNS through infrastructure checks:
# 1. Confirm pod resolv.conf nameserver matches kube-dns ClusterIP
DNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
echo "kube-dns ClusterIP: ${DNS_IP}"
# 2. Verify CoreDNS is serving queries (check logs for recent activity)
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50 --since=5m
# 3. Check CoreDNS health endpoint from within a CoreDNS pod
COREDNS_POD=$(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n kube-system ${COREDNS_POD} -- wget -qO- http://localhost:8080/health 2>&1
kubectl exec -n kube-system ${COREDNS_POD} -- wget -qO- http://localhost:8181/ready 2>&1
# 4. Check for DNS-related errors in application pod logs
kubectl logs -n ${NS} ${POD} --tail=200 | grep -iE 'dns|resolve|NXDOMAIN|lookup|name.resolution|no.such.host|could.not.resolve'Headless Service Verification
Headless services (ClusterIP: None) return individual pod IPs instead of a single VIP.
# Verify headless service has endpoints (indirect check — no exec needed)
kubectl get endpoints ${HEADLESS_SVC} -n ${NAMESPACE} -o jsonpath='{.subsets[*].addresses[*].ip}'
# If exec is available, resolve and confirm multiple A records
kubectl exec -n ${NS} ${POD} -- nslookup ${HEADLESS_SVC}.${NAMESPACE}.svc.cluster.local
# Individual pod DNS for StatefulSets
# Format: ${POD_NAME}.${HEADLESS_SVC}.${NAMESPACE}.svc.cluster.local
kubectl exec -n ${NS} ${POD} -- nslookup ${STATEFULSET_POD}-0.${HEADLESS_SVC}.${NAMESPACE}.svc.cluster.localInterpreting Results
| Result | Meaning |
| Returns IP address | Resolution working |
** server can't find ... NXDOMAIN | Name does not exist — wrong service name, namespace, or missing service |
bad address / could not resolve host | DNS lookup failed — CoreDNS unreachable or misconfigured |
connection timed out; no servers could be reached | Cannot reach CoreDNS — network or service issue |
SERVFAIL | CoreDNS received the query but failed to resolve — check Corefile and upstream |
Section 3: CoreDNS Configuration (Corefile)
The Corefile is stored as a ConfigMap and defines how CoreDNS handles queries.
# View the Corefile
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}'
# View with line numbers for reference
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | cat -n
# Check if a custom ConfigMap exists
kubectl get configmap coredns-custom -n kube-system 2>/dev/nullDefault Corefile Structure
A healthy default Corefile looks like:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}Key Plugins and What Can Go Wrong
| Plugin | Purpose | Common Issue |
kubernetes | Resolves cluster.local names | Wrong cluster domain, pods setting, or missing fallthrough |
forward | Sends non-cluster queries upstream | Wrong upstream address, unreachable forwarder |
cache | Caches responses | TTL too high causes stale answers; too low overloads upstream |
loop | Detects forwarding loops | CoreDNS restarts if loop detected — check /etc/resolv.conf on nodes |
errors | Logs errors | Missing plugin hides failures |
health | Liveness endpoint on :8080 | If missing, kubelet cannot health-check CoreDNS |
ready | Readiness endpoint on :8181 | If missing, endpoints not populated until pod is actually ready |
reload | Hot-reload on Corefile change | Without this, ConfigMap changes require pod restart |
Common Corefile Misconfigurations
| Problem | Symptom | Diagnostic |
| Wrong cluster domain | All internal lookups return NXDOMAIN | Check if kubernetes zone matches --cluster-domain kubelet flag |
forward . 8.8.8.8 when nodes use private DNS | External resolution of private zones fails | Check if forward directive should use /etc/resolv.conf or a private upstream |
Missing loop plugin | CoreDNS forwards to itself via node resolv.conf, infinite loop | Check if loop plugin is present in Corefile |
pods verified instead of pods insecure | Pod A/AAAA records fail verification checks | Check if pods setting is verified — recommend pods insecure unless strict verification is needed |
| Custom forward zone typo | Queries for that zone fail | Verify zone block spelling and upstream IP |
Missing fallthrough in kubernetes block | Reverse lookups for pod IPs fail | Check if fallthrough in-addr.arpa ip6.arpa is present |
Section 4: Pod DNS Configuration
Each pod gets its DNS config from the kubelet, controlled by the pod's dnsPolicy field.
dnsPolicy Options
| Policy | Behaviour | When Used |
ClusterFirst (default) | Use CoreDNS for all lookups. Falls back to upstream for non-cluster names | Standard workloads |
Default | Use the node's /etc/resolv.conf directly — bypasses CoreDNS | Pods that must use node DNS (rare) |
None | No auto-config — requires dnsConfig to specify nameservers | Custom DNS setups |
ClusterFirstWithHostNet | Like ClusterFirst but for hostNetwork: true pods | Pods using host networking that still need cluster DNS |
Checking Pod DNS Config
# View the resolv.conf inside a running pod
kubectl exec ${POD} -n ${NS} -- cat /etc/resolv.conf
# Check the dnsPolicy on a pod spec
kubectl get pod ${POD} -n ${NS} -o jsonpath='{.spec.dnsPolicy}'
# Check dnsConfig overrides
kubectl get pod ${POD} -n ${NS} -o jsonpath='{.spec.dnsConfig}' | jq .Expected resolv.conf for ClusterFirst
nameserver 10.96.0.10 # kube-dns ClusterIP
search ${NS}.svc.cluster.local svc.cluster.local cluster.local
options ndots:5The ndots Problem
With ndots:5 (the default), any name with fewer than 5 dots triggers search domain expansion. A lookup for api.example.com (2 dots, fewer than 5) generates these queries in order:
api.example.com.${NS}.svc.cluster.local— NXDOMAINapi.example.com.svc.cluster.local— NXDOMAINapi.example.com.cluster.local— NXDOMAINapi.example.com.— success (finally resolves)
This means 4 DNS queries instead of 1 for every external name, adding latency and load.
If ndots is the cause, recommend to the user:
- Use trailing dots in application config (FQDN):
api.example.com.bypasses search domains entirely - Reduce ndots per pod via
spec.dnsConfig.options(e.g.,ndots: "2")
Common Pod DNS Issues
| Problem | Symptom | Diagnostic |
dnsPolicy: Default on a non-hostNetwork pod | Pod uses node DNS, cannot resolve cluster services | Check dnsPolicy — recommend changing to ClusterFirst |
hostNetwork: true without ClusterFirstWithHostNet | Pod uses node DNS, skips CoreDNS | Check dnsPolicy — recommend ClusterFirstWithHostNet |
dnsPolicy: None without dnsConfig | Pod has no nameservers, all lookups fail | Check if dnsConfig.nameservers is defined |
| High ndots with many external calls | Slow DNS, high query volume to CoreDNS | Check ndots value — recommend reducing ndots or using FQDNs with trailing dot |
Section 5: Network Connectivity to DNS
If CoreDNS is running and configured correctly but pods still cannot resolve, the network path may be blocked.
# Verify the kube-dns ClusterIP is reachable from an existing pod
DNS_IP=$(kubectl get svc kube-dns -n kube-system -o jsonpath='{.spec.clusterIP}')
kubectl exec -n ${NS} ${POD} -- nslookup kubernetes.default.svc.cluster.local ${DNS_IP}
# If nslookup unavailable, test connectivity with wget to CoreDNS health endpoint
kubectl exec -n ${NS} ${POD} -- wget -qO- --timeout=3 http://${DNS_IP}:8080/health 2>&1
# Check if network policies in the pod's namespace block DNS egress
kubectl get networkpolicies -n ${NS}
# Look for policies that restrict egress
kubectl get networkpolicies -n ${NS} -o json | \
jq -r '.items[] | select(.spec.policyTypes[] == "Egress") | .metadata.name'Network Policy DNS Gotcha
If a namespace has an egress NetworkPolicy, it blocks all egress traffic by default — including DNS to kube-system. Check whether existing egress policies include a rule allowing UDP/TCP port 53. If they don't, recommend the user add a DNS egress rule to their NetworkPolicy.
Node-Level Issues
# Check iptables rules for kube-dns Service (on the node)
# The kube-dns ClusterIP should have DNAT rules pointing to CoreDNS pod IPs
iptables-save | grep kube-dns
# Check kube-proxy is running (manages ClusterIP rules)
kubectl get pods -n kube-system -l k8s-app=kube-proxy
# Check conntrack table for DNS (UDP conntrack issues cause intermittent failures)
conntrack -L -p udp --dport 53 2>/dev/null | head -20Intermittent DNS Failures (Race Condition)
A known Linux kernel issue causes intermittent DNS failures when a pod sends UDP packets to a ClusterIP and both the A and AAAA queries race through conntrack. Symptoms: occasional 5-second timeouts on DNS lookups.
If this race condition is confirmed, recommend one of these approaches to the user:
| Approach | Recommendation |
| Use TCP for DNS | Recommend setting use-vc option in pod dnsConfig |
| NodeLocal DNSCache | Recommend deploying node-level DNS cache (eliminates conntrack for DNS) |
| Single-request-reopen | Recommend setting single-request-reopen option in pod dnsConfig |
Section 6: CoreDNS Logs and Metrics
Enabling the Log Plugin
By default, CoreDNS does not log every query. To enable verbose logging for debugging, add the log plugin to the Corefile:
# Check if log plugin is already enabled
kubectl get configmap coredns -n kube-system -o jsonpath='{.data.Corefile}' | grep -c "log"If verbose logging is needed, recommend the user add log inside the Corefile server block (CoreDNS reloads automatically if the reload plugin is present).
Reading CoreDNS Logs
# Recent logs
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=200
# Filter for errors
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=500 | grep -iE 'error|fail|refused|timeout|SERVFAIL|NXDOMAIN'
# Watch live (useful during testing)
kubectl logs -n kube-system -l k8s-app=kube-dns -f --tail=50Key Log Patterns
| Log Pattern | Meaning | Diagnostic |
plugin/loop: Loop ... detected | CoreDNS is forwarding to itself | Check node /etc/resolv.conf and upstream forwarder config for loops |
plugin/forward: no upstreams available | All upstream DNS servers unreachable | Check upstream IPs in Corefile forward directive |
SERVFAIL | CoreDNS could not resolve the query | Check upstream forwarder or zone configuration |
NXDOMAIN | Domain does not exist | Verify the queried name is correct |
i/o timeout | Upstream DNS server not responding | Check network path to upstream; upstream may be overloaded |
connection refused | Upstream DNS server rejecting connections | Check if upstream IP is correct and upstream is running |
OOMKilled (in events, not logs) | CoreDNS ran out of memory | Check memory limits; recommend increasing memory limits |
CoreDNS Metrics
CoreDNS exposes Prometheus metrics on port 9153 (when the prometheus plugin is enabled).
# Check metrics from within a CoreDNS pod (no new workloads needed)
COREDNS_POD=$(kubectl get pods -n kube-system -l k8s-app=kube-dns -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n kube-system ${COREDNS_POD} -- wget -qO- http://localhost:9153/metrics 2>&1 | head -50Key metrics to monitor:
| Metric | What It Tells You |
coredns_dns_requests_total | Total query volume — watch for spikes |
coredns_dns_responses_total{rcode="SERVFAIL"} | Server failures — should be near zero |
coredns_dns_responses_total{rcode="NXDOMAIN"} | Non-existent domains — high count suggests misconfigured clients |
coredns_forward_requests_total | Upstream forwarding volume |
coredns_forward_responses_total{rcode="SERVFAIL"} | Upstream failures |
coredns_dns_request_duration_seconds | Query latency — p99 should be under 100ms |
coredns_cache_hits_total / coredns_cache_misses_total | Cache effectiveness |
coredns_panics_total | CoreDNS panics — should be zero |
Common Mistakes
| Mistake | Why It Fails | Instead |
Testing DNS with ping instead of nslookup | ping failure may be ICMP blocked, not DNS | Use nslookup, getent hosts, or dig via kubectl exec to test DNS specifically |
| Deploying test pods to diagnose DNS | May lack permissions, creates resources, adds cleanup burden | Use kubectl exec into existing pods or rely on indirect checks (CoreDNS logs, health endpoints) |
| Assuming DNS is broken when the Service has no endpoints | DNS resolves the ClusterIP correctly, but no pods back the Service | Check kubectl get endpoints before blaming DNS |
Editing the CoreDNS ConfigMap without the reload plugin | Changes take effect only after pod restart | Verify reload is in the Corefile, or restart CoreDNS pods after edits |
Using dnsPolicy: Default and expecting cluster DNS to work | Default uses the node's DNS, not CoreDNS | Check dnsPolicy — recommend ClusterFirst for pods that need service discovery |
| Ignoring ndots when debugging slow external lookups | Each external name generates up to 5 queries with ndots:5 | Check ndots value; recommend reducing ndots or using FQDNs with trailing dot |
| Adding a NetworkPolicy without a DNS egress rule | All DNS blocked — every lookup times out | Check egress policies for missing UDP/TCP 53 rules |
| Restarting CoreDNS as the first troubleshooting step | Hides the evidence (logs, metrics) needed for diagnosis | Collect logs and check configuration before restarting |
MCP Tools Available
When the appropriate MCP servers are connected, prefer these over raw kubectl where available:
mcp__flux-operator-mcp__get_kubernetes_resources- Query CoreDNS deployment, pods, services, endpoints, configmapsmcp__flux-operator-mcp__get_kubernetes_logs- Retrieve CoreDNS pod logsmcp__flux-operator-mcp__get_kubernetes_metrics- Check CoreDNS resource consumption
Behavioural Guidelines
- Run the health check first — Always complete the Quick DNS Health Check before deep-diving. Present the summary to the user so they see the overall picture.
- Never deploy test workloads — Use
kubectl execinto existing running pods for DNS testing. If no pods have DNS tools, use indirect methods (resolv.conf inspection, CoreDNS logs, health endpoints). - Distinguish DNS failure from service failure — If DNS resolves but the service is unreachable, the problem is not DNS. Check endpoints and connectivity.
- Check the Corefile before recommending changes — Read and understand the current configuration before recommending modifications to the user.
- Never expose secrets — List ConfigMap and Secret names. Never decode or print secret values.
- Fall back to indirect checks — When
kubectl execis unavailable or pod images lack tools, CoreDNS health endpoints, logs, and infrastructure state (endpoints, service, pod status) provide strong evidence of DNS health. - Consider ndots for performance issues — Slow DNS is often not a CoreDNS problem but an ndots/search-domain expansion issue.
- Collect logs before restarting — CoreDNS logs are ephemeral. Capture them before any restart.
- Check network policies early — A missing DNS egress rule is one of the most common causes of DNS failure in policy-enforced namespaces.
- Recommend scaling before tuning — If CoreDNS is overloaded, recommend adding replicas before adjusting cache sizes or Corefile plugins.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

