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.

App Pod /etc/resolv.conf DNS query CoreDNS kube-dns Service 10.96.0.10:53 watch API Server Upstream DNS (8.8.8.8) non-cluster queries nameserver 10.96.0.10 search default.svc.cluster.local

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
FieldPurpose
nameserverCoreDNS Service IP (kube-dns Service in kube-system)
searchDomain suffixes to try (enables short names like webweb.default.svc.cluster.local)
ndots:5If 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 TypeDNS RecordReturns
ClusterIPA/AAAA recordClusterIP (single IP)
Headless (clusterIP: None)A/AAAA recordAll Pod IPs (multiple A records)
ExternalNameCNAME recordExternal hostname
Any (with named ports)SRV recordPort + 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)
The StatefulSet DNS pattern: {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
}
CKA exam: you may be asked to add a stub domain to CoreDNS. Edit the ConfigMap: 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:

dnsPolicyBehaviorUse Case
ClusterFirst (default)Use CoreDNS. Non-cluster names forwarded upstream.Normal Pods
DefaultInherit the node's /etc/resolv.confPods that need node's DNS (rare)
NoneNo auto-config. Must provide dnsConfig manually.Custom DNS setups
ClusterFirstWithHostNetLike ClusterFirst, but for Pods using hostNetworkDaemonSets 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
For high-throughput services making many external DNS lookups, reducing 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

SymptomLikely CauseFix
All DNS lookups failCoreDNS Pods down or unreachableCheck kubectl get pods -n kube-system -l k8s-app=kube-dns
Service name doesn't resolveWrong namespace, typo, Service doesn't existUse FQDN: svc.namespace.svc.cluster.local
External names very slowndots:5 causing 4 extra queriesUse FQDN with trailing dot or reduce ndots
Intermittent DNS failuresCoreDNS overloaded (too few replicas)Scale CoreDNS: kubectl scale deploy coredns -n kube-system --replicas=3
Pod resolves but can't connectDNS works but NetworkPolicy blocks trafficCheck NetworkPolicies, Service endpoints
For CKA DNS debugging: (1) Run 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

ConceptKey Point
CoreDNSCluster DNS server — Deployment in kube-system, watches API server
Service DNS{name}.{namespace}.svc.cluster.local → ClusterIP
Headless DNSReturns 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 domainsEnable short names (webweb.default.svc.cluster.local)
ndots:5Names with <5 dots try search path first — causes extra queries
Stub domainsForward specific zones to custom DNS (Consul, corporate DNS)
dnsPolicyClusterFirst (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.billingpayments.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?

4 queries (3 failures + 1 success). 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?

Normal Service: Returns a single A record — the ClusterIP (e.g., 10.96.0.50).
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?

Checklist: (1) Does the Pod's /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?

Add a stub domain to the CoreDNS ConfigMap. Add a server block:
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?

With 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.