CoreDNS in Kubernetes
Introduction¶
CoreDNS runs as a Deployment in kube-system and is the cluster's DNS
server. Pods get its Service IP as their nameserver (via the kubelet), and it
resolves Service.namespace.svc.cluster.local names plus forwards everything
else upstream.
The Corefile¶
.: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 {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
kubernetesplugin — answers*.cluster.localfrom the API (Services, Endpoints, and pod A records).forward . /etc/resolv.conf— anything notcluster.localgoes to the node's upstream resolvers. Point it at explicit resolvers (forward . 1.1.1.1 9.9.9.9) if the node's resolv.conf is unreliable.cache 30— 30s positive/negative cache.reload— picks up ConfigMap changes without a restart (~2 min).
The ndots:5 problem¶
The kubelet writes this into every pod's /etc/resolv.conf:
search team-a.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
ndots:5 means: if the name has fewer than 5 dots, try each search domain
first before trying it as-is. So curl https://api.example.com (2 dots)
generates:
api.example.com.team-a.svc.cluster.local -> NXDOMAIN
api.example.com.svc.cluster.local -> NXDOMAIN
api.example.com.cluster.local -> NXDOMAIN
api.example.com -> the real answer (finally)
Four queries (×2 for A+AAAA) for one external hostname. On a busy pod this hammers CoreDNS and adds latency.
Mitigations:
- Use fully-qualified names with a trailing dot in app config:
api.example.com.— 0 extra lookups. - Set
dnsConfigon the pod to lowerndots(e.g.ndots: 2) — but then short in-cluster names likeapi(same namespace) still work via the search list, whileapi.team-bneeds the fullapi.team-b.svc.cluster.local. - NodeLocal DNSCache (below) — the biggest win.
NodeLocal DNSCache¶
A DaemonSet running a CoreDNS cache on every node, listening on a link-local IP
(169.254.20.10). Pods query the local cache first:
- Cache hits never leave the node → huge latency and load reduction.
- Uses TCP to the upstream CoreDNS (avoids conntrack UDP races that caused the infamous 5s DNS timeouts on some kernels).
- Cache misses forward to the cluster CoreDNS.
Deploy it (the upstream manifest) and update the kubelet's --cluster-dns or
use the interface-based setup. Standard on managed clusters as an add-on.
Tuning for scale¶
- Replicas / autoscaling —
cluster-proportional-autoscalerscales CoreDNS with node/core count. Default 2 replicas is too few for a big cluster. - Resources — give CoreDNS a real CPU request; under-provisioned CoreDNS = cluster-wide slow DNS.
cache— raise TTL if your Services are stable.- Disable AAAA for IPv4-only clusters with the
templateplugin to cut query volume in half (pods still ask for both).
Verification and troubleshooting¶
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50
kubectl -n kube-system get cm coredns -o yaml
kubectl run t --rm -it --image=nicolaka/netshoot -- bash
nslookup kubernetes.default
nslookup api.team-a.svc.cluster.local
dig +search +short api.example.com # see the search-domain expansion
cat /etc/resolv.conf
- All in-cluster DNS fails — CoreDNS pods down/crashlooping (check the
Corefile for a syntax error —
reloadlogs it), or a NetworkPolicy blocking UDP/TCP 53 tokube-system, or thekube-dnsService has no endpoints. nslookup: connection timed out; no servers could be reached— the pod's/etc/resolv.confnameserver (kube-dns Service IP) isn't reachable: kube-proxy issue, NetworkPolicy, or CNI.- Intermittent 5s stalls on DNS — the classic conntrack UDP race
(
net.netfilter.nf_conntrackinsert races). NodeLocal DNSCache (TCP upstream) fixes it; oroptions single-request-reopenindnsConfig. - External lookups slow, internal fine —
ndots:5amplification. FQDN with trailing dot, or NodeLocal DNSCache, or lowerndots. loopplugin crashes CoreDNS on start — the node's/etc/resolv.confpoints back at CoreDNS (a resolution loop). Forward to explicit upstream IPs instead of/etc/resolv.conf.- Stale Service IP resolved after a Service was recreated — cache TTL; wait
cacheseconds or flush. Apps that cache DNS forever (some JVM setups) neednetworkaddress.cache.ttltuned.
Related tools and reading¶
- On-site: DNS lookup, Port Checker.
- Related posts: Kubernetes Services and Endpoints, resolv.conf and systemd-resolved.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Docker & Kubernetes
StatefulSets Explained
StatefulSets give pods sticky names, sticky storage, and ordered operations — what databases and clustered apps need. Here's how each guarantee works.
October 24, 2026Persistent Volumes and Claims, Explained
PVC is a request, PV is the fulfilled storage, StorageClass is the factory. Most 'Pending PVC' problems are an access-mode or provisioner mismatch.
October 24, 2026Kubernetes Jobs and CronJobs
A Job runs pods until they succeed; a CronJob creates Jobs on a schedule. Here's parallelism, retry limits, the missed-schedule trap, and history cleanup.
October 24, 2026