DNS is the backbone of service discovery in Kubernetes. Every Service and Pod gets a DNS name, and CoreDNS handles resolution within the cluster. Understanding DNS naming, record types, and debugging is essential for production troubleshooting — DNS issues are one of the top 3 causes of connectivity failures.
1. CoreDNS Architecture
CoreDNS is deployed as a Deployment + Service in kube-system. It watches the API server for Services and Endpoints and serves DNS records accordingly.
How Pods Find CoreDNS
The kubelet configures every Pod's /etc/resolv.conf to point at the CoreDNS Service IP:
# Inside any Pod: cat /etc/resolv.conf nameserver 10.96.0.10 # CoreDNS ClusterIP search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
| Field | Purpose |
|---|---|
nameserver | CoreDNS Service IP (kube-dns Service in kube-system) |
search | Domain suffixes to try (enables short names like web → web.default.svc.cluster.local) |
ndots:5 | If a name has fewer than 5 dots, try search domains first before treating as FQDN |
ndots:5 causes extra DNS queries. When a Pod looks up api.example.com (2 dots < 5), it first tries api.example.com.default.svc.cluster.local, then api.example.com.svc.cluster.local, then api.example.com.cluster.local, and finally api.example.com. (absolute). That's 4 queries for an external name! For high-traffic external lookups, append a trailing dot (api.example.com.) to skip the search path.
2. DNS Record Types
Service DNS Names
# Full format:
{service-name}.{namespace}.svc.{cluster-domain}
# Examples:
web.default.svc.cluster.local # Service "web" in "default" namespace
postgres.database.svc.cluster.local # Service "postgres" in "database" namespace
Short Names & Search Domains
# From a Pod in the "default" namespace: curl web # → web.default.svc.cluster.local (same namespace) curl web.database # → web.database.svc.cluster.local (different namespace) curl web.database.svc # → web.database.svc.cluster.local # The search path in resolv.conf makes these work: # search default.svc.cluster.local svc.cluster.local cluster.local
Record Types by Service Type
| Service Type | DNS Record | Returns |
|---|---|---|
| ClusterIP | A/AAAA record | ClusterIP (single IP) |
| Headless (clusterIP: None) | A/AAAA record | All Pod IPs (multiple A records) |
| ExternalName | CNAME record | External hostname |
| Any (with named ports) | SRV record | Port + hostname for service discovery |
SRV Records
# Format: _port-name._protocol.service.namespace.svc.cluster.local # Example for Service "web" with port named "http": dig SRV _http._tcp.web.default.svc.cluster.local # Returns: # _http._tcp.web.default.svc.cluster.local. 30 IN SRV 0 100 80 web.default.svc.cluster.local. # priority weight port target # SRV records are useful for service meshes and clients that # need to discover both the port AND the address dynamically
Pod DNS Records
# Pods don't normally get DNS records unless: # 1. Via a headless Service + StatefulSet: # postgres-0.postgres-headless.default.svc.cluster.local # 2. Via Pod spec with hostname and subdomain: spec: hostname: my-pod subdomain: my-subdomain # must match a headless Service name # Creates: my-pod.my-subdomain.default.svc.cluster.local # 3. Pod A-record (enabled by default, rarely used): # 10-244-1-5.default.pod.cluster.local (dashes replace dots in IP)
{pod-name}.{headless-service}.{namespace}.svc.cluster.local. This is how distributed databases discover peers — each member connects to others by their stable DNS names, which persist across Pod rescheduling.
3. CoreDNS Configuration
CoreDNS is configured via a ConfigMap in kube-system:
kubectl get configmap coredns -n kube-system -o yaml
Default Corefile
.:53 {
errors # Log errors
health { # Health check endpoint (/health)
lameduck 5s
}
ready # Readiness endpoint (/ready)
kubernetes cluster.local in-addr.arpa ip6.arpa { # K8s plugin
pods insecure # Enable Pod A records
fallthrough in-addr.arpa ip6.arpa
ttl 30 # DNS TTL for records
}
prometheus :9153 # Metrics endpoint
forward . /etc/resolv.conf { # Forward non-cluster queries upstream
max_concurrent 1000
}
cache 30 # Cache responses for 30s
loop # Detect forwarding loops
reload # Auto-reload Corefile on change
loadbalance # Round-robin A records
}
Common Customizations
Stub Domains — Forward specific zones to custom DNS
# Add to Corefile: forward queries for .consul to Consul DNS
consul.local:53 {
errors
cache 30
forward . 10.0.0.100:8600 # Consul DNS agent
}
# Now Pods can resolve: my-service.service.consul.local
Custom Upstream DNS
# Replace /etc/resolv.conf forwarding with specific servers:
forward . 8.8.8.8 8.8.4.4 {
max_concurrent 1000
}
Hosts Plugin — Static entries
# Add custom host entries (like /etc/hosts):
hosts {
10.0.0.50 legacy-db.company.internal
fallthrough
}
kubectl edit configmap coredns -n kube-system. CoreDNS auto-reloads (the reload plugin). No Pod restart needed.
4. Pod DNS Policies
Control how a Pod's /etc/resolv.conf is generated:
| dnsPolicy | Behavior | Use Case |
|---|---|---|
ClusterFirst (default) | Use CoreDNS. Non-cluster names forwarded upstream. | Normal Pods |
Default | Inherit the node's /etc/resolv.conf | Pods that need node's DNS (rare) |
None | No auto-config. Must provide dnsConfig manually. | Custom DNS setups |
ClusterFirstWithHostNet | Like ClusterFirst, but for Pods using hostNetwork | DaemonSets with hostNetwork: true |
Custom DNS Config
spec:
dnsPolicy: None
dnsConfig:
nameservers:
- 10.0.0.100 # Custom DNS
searches:
- mycompany.internal
- svc.cluster.local
options:
- name: ndots
value: "2" # Reduce ndots for fewer search queries
ndots from 5 to 2 (or appending trailing dots to FQDNs) can significantly reduce DNS query volume. At scale, unnecessary DNS queries can overwhelm CoreDNS — monitor coredns_dns_requests_total metrics.
5. Debugging DNS Issues
# Test DNS resolution from a debug Pod: kubectl run dnstest --image=busybox:1.36 -it --rm -- nslookup web.default.svc.cluster.local # More detailed (with dig): kubectl run dnstest --image=nicolaka/netshoot -it --rm -- \ dig web.default.svc.cluster.local # Check if CoreDNS is running: kubectl get pods -n kube-system -l k8s-app=kube-dns # Check CoreDNS logs for errors: kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50 # Verify the kube-dns Service exists: kubectl get svc kube-dns -n kube-system # Should show ClusterIP 10.96.0.10 # Check Pod's resolv.conf: kubectl exec my-pod -- cat /etc/resolv.conf
Common DNS Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| All DNS lookups fail | CoreDNS Pods down or unreachable | Check kubectl get pods -n kube-system -l k8s-app=kube-dns |
| Service name doesn't resolve | Wrong namespace, typo, Service doesn't exist | Use FQDN: svc.namespace.svc.cluster.local |
| External names very slow | ndots:5 causing 4 extra queries | Use FQDN with trailing dot or reduce ndots |
| Intermittent DNS failures | CoreDNS overloaded (too few replicas) | Scale CoreDNS: kubectl scale deploy coredns -n kube-system --replicas=3 |
| Pod resolves but can't connect | DNS works but NetworkPolicy blocks traffic | Check NetworkPolicies, Service endpoints |
nslookup from a Pod (busybox or netshoot). (2) Use the FQDN format to eliminate search domain issues. (3) Check CoreDNS Pod health. (4) Check the CoreDNS ConfigMap for typos. These four steps solve 95% of exam DNS questions.
Summary
| Concept | Key Point |
|---|---|
| CoreDNS | Cluster DNS server — Deployment in kube-system, watches API server |
| Service DNS | {name}.{namespace}.svc.cluster.local → ClusterIP |
| Headless DNS | Returns all Pod IPs (multiple A records) |
| StatefulSet DNS | {pod}.{headless-svc}.{ns}.svc.cluster.local → specific Pod IP |
| SRV records | _port._proto.svc.ns.svc.cluster.local — port discovery |
| Search domains | Enable short names (web → web.default.svc.cluster.local) |
| ndots:5 | Names with <5 dots try search path first — causes extra queries |
| Stub domains | Forward specific zones to custom DNS (Consul, corporate DNS) |
| dnsPolicy | ClusterFirst (default), Default, None, ClusterFirstWithHostNet |
📝 Quiz: DNS & Service Discovery
Q1: A Pod in namespace "orders" wants to reach Service "payments" in namespace "billing". What DNS name should it use?
payments.billing (short form) or payments.billing.svc.cluster.local (FQDN). The short form works because the search domain svc.cluster.local is in the Pod's resolv.conf, so it expands payments.billing → payments.billing.svc.cluster.local.Q2: A Pod looks up api.github.com. With ndots:5, how many DNS queries are sent before the correct answer is found?
api.github.com has 2 dots (< 5 ndots), so search path is tried first: (1) api.github.com.default.svc.cluster.local NXDOMAIN, (2) api.github.com.svc.cluster.local NXDOMAIN, (3) api.github.com.cluster.local NXDOMAIN, (4) api.github.com. → success. Fix: use api.github.com. (trailing dot) to skip the search path.Q3: You query the DNS name of a headless Service. What do you get back compared to a normal ClusterIP Service?
Headless Service: Returns multiple A records — one per ready Pod (e.g., 10.244.1.5, 10.244.2.8, 10.244.3.2). The client receives all Pod IPs and can choose how to connect.
Q4: CoreDNS is running but a Pod can't resolve web.default.svc.cluster.local. The Service exists. What do you check?
/etc/resolv.conf point to CoreDNS IP? (check dnsPolicy). (2) Can the Pod reach CoreDNS IP on port 53? (NetworkPolicy might block kube-system). (3) Does the Service have endpoints? (kubectl get endpoints web — if empty, no Pods match the selector). (4) Check CoreDNS logs for errors.Q5: You need Pods to resolve myapp.service.consul from a Consul DNS running at 10.0.0.100:8600. How do you configure this?
consul:53 {
errors
cache 30
forward . 10.0.0.100:8600
}CoreDNS auto-reloads. Queries ending in .consul will be forwarded to the Consul DNS agent.Q6: A DaemonSet uses hostNetwork: true. Its Pods can't resolve cluster Service names. Why and how do you fix it?
hostNetwork: true, the Pod uses the node's network namespace, including the node's /etc/resolv.conf — which points to the node's DNS (not CoreDNS). Fix: set dnsPolicy: ClusterFirstWithHostNet. This forces the kubelet to configure the Pod's resolv.conf to use CoreDNS even when using the host network.