Interactive Guide to Understanding Service Discovery and DNS Resolution
DNS Query Resolution Flow
Click on each step to see detailed information about the DNS resolution process.
1
Pod Initiates DNS Query
Application in Pod requests DNS resolution for service nameService names can be short (my-nginx) or fully qualified (my-nginx.default.svc.cluster.local)
The kubelet configures each Pod's DNS settings, pointing to CoreDNS cluster IP.
↓
2
Query Sent to CoreDNS
DNS query is forwarded to CoreDNSCoreDNS is the cluster DNS server running in kube-system namespace, replacing the older kube-dns service (kube-dns)
CoreDNS Service:
kubectl get svc kube-dns -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
kube-dns ClusterIP 10.32.0.10 <none> 53/UDP,53/TCP
CoreDNS pods receive the query through the kube-dns service endpoint.
↓
3
CoreDNS Processes Query
CoreDNS expands short names using search domainsSearch domains allow using short names (my-nginx) which expand to FQDNs (my-nginx.default.svc.cluster.local) and queries Kubernetes API
Query Expansion Process:
my-nginx → my-nginx.default.svc.cluster.local
If not found → my-nginx.svc.cluster.local
If not found → my-nginx.cluster.local
CoreDNS uses the Kubernetes plugin to query the API server for Service and Endpoint information.
↓
4
Service Lookup
CoreDNS queries Kubernetes API to find the ServiceServices provide stable DNS names and IP addresses for dynamic sets of Pods matching the DNS name
Service Resolution Types:
Normal Service (ClusterIP): Returns the Service's cluster IP address
Headless Service (clusterIP: None): Returns all Pod IPs directly
ExternalName Service: Returns a CNAME record to external DNS name
# Normal Service
my-nginx.default.svc.cluster.local → 10.100.200.50 (ClusterIP)
# Headless Service
my-nginx.default.svc.cluster.local → 172.17.0.3, 172.17.0.4 (Pod IPs)
↓
5
Endpoint Resolution
For normal Services, EndpointsEndpoints track the IP addresses of Pods backing a Service are checked to find backing Pod IPs
Endpoint Lookup:
kubectl get endpoints my-nginx
NAME ENDPOINTS AGE
my-nginx 172.17.0.3:80,172.17.0.4:80 5m
While DNS returns the ClusterIP, traffic to that IP is load-balanced by kube-proxy to the Pod IPs listed in Endpoints.
↓
6
DNS Response Returned
CoreDNS returns DNS response with A/AAAA recordA records map names to IPv4 addresses, AAAA records map to IPv6 addresses containing IP address(es)
DNS Response Format:
;; QUESTION SECTION:
;my-nginx.default.svc.cluster.local. IN A
;; ANSWER SECTION:
my-nginx.default.svc.cluster.local. 30 IN A 10.100.200.50
The Pod receives the IP address and can now connect to the Service.
↓
7
Connection Established
Pod uses resolved IP to connect; kube-proxykube-proxy manages network rules for Service traffic routing and load balancing routes traffic to healthy Pod
Traffic Flow:
Pod connects to ClusterIP (10.100.200.50:80)
kube-proxy intercepts traffic via iptables/IPVS rules
Traffic is load-balanced to one of the healthy backend Pods
Connection established with selected Pod (172.17.0.3:80 or 172.17.0.4:80)
Key Point
DNS resolution is separate from traffic routing. DNS provides the Service IP, while kube-proxy handles the actual Pod selection and load balancing.
DNS Naming Conventions in Kubernetes
FQDN Breakdown
my-nginx.default.svc.cluster.local
my-nginx
Service Name
default
Namespace
svc
Service Indicator
cluster.local
Cluster Domain
Namespace Isolation
Queries without namespace specification are limited to the Pod's namespace. To access services in other namespaces, specify the namespace:
data - Only searches in current namespace
data.prod - Accesses service in 'prod' namespace
data.prod.svc.cluster.local - Fully qualified domain name
Service A/AAAA Records
Format:service-name.namespace.svc.cluster-domain
# Normal Service (ClusterIP)
my-nginx.default.svc.cluster.local
→ Returns: 10.100.200.50
# Headless Service (clusterIP: None)
my-nginx.default.svc.cluster.local
→ Returns: 172.17.0.3, 172.17.0.4, 172.17.0.5
Normal services return a single stable ClusterIP. Headless services return all Pod IPs directly for client-side load balancing.
# Named port SRV record
_http._tcp.my-nginx.default.svc.cluster.local
→ Returns: port + domain name
# For headless service
_http._tcp.my-nginx.default.svc.cluster.local
→ Returns: Multiple records with Pod hostnames
SRV records enable service discovery for named ports, providing both port numbers and target hostnames.
Pod A/AAAA Records
Format:pod-ip.namespace.pod.cluster-domain
# Pod with IP 172.17.0.3
172-17-0-3.default.pod.cluster.local
→ Returns: 172.17.0.3
# Service-scoped Pod record
172-17-0-3.my-nginx.default.svc.cluster.local
→ Returns: 172.17.0.3
Pod DNS records use dashes instead of dots in IP addresses. These are automatically created by CoreDNS.
dnsconfig.yaml — dnsPolicy: None with custom nameservers, searches, and ndots
The dnsPolicy field in Pod spec determines how DNS resolution is configured. Choose the appropriate policy based on your networking requirements.
ClusterFirst
DEFAULT
Uses cluster DNS (CoreDNS) for resolution. Any DNS query that does not match the cluster domain suffix is forwarded to the upstream nameserver inherited from the node.
Use Case: Pods using host networking that still need to resolve Kubernetes Services (e.g., monitoring agents, network tools).
None
Ignores all DNS settings from the cluster. Requires explicit dnsConfig to specify custom DNS configuration. Provides complete control over DNS settings.
Use Case: Testing custom DNS resolvers, specific DNS requirements, or integration with external DNS systems.
Example: Pod with hostNetwork and ClusterFirstWithHostNet
apiVersion: v1
kind: Pod
metadata:name: busybox-hostnetwork
spec:containers:
- name: busybox
image: busybox:1.28
command: ["sleep", "3600"]
hostNetwork:truednsPolicy: ClusterFirstWithHostNet
# This configuration:
# - Pod uses host's network stack (shares host IP)
# - But still resolves Kubernetes Services via CoreDNS
# - Can resolve both cluster Services and external DNS names
Example: Pod with Custom DNS Configuration (dnsPolicy: None)
Server: 10.32.0.10
Address: 10.32.0.10#53
** server can't find data: NXDOMAIN
Scenario 1: Cross-Namespace Service Access
# Pod in 'test' namespace trying to access service in 'prod' namespace# ❌ FAILS - searches only in test namespace
curl data
# ✅ SUCCESS - specifies prod namespace
curl data.prod
# ✅ SUCCESS - uses full FQDN
curl data.prod.svc.cluster.local
# DNS Query Expansion:
# Query: data
# 1. data.test.svc.cluster.local (NOT FOUND)
# 2. data.svc.cluster.local (NOT FOUND)
# 3. data.cluster.local (NOT FOUND)
# Result: NXDOMAIN
# Query: data.prod
# 1. data.prod.test.svc.cluster.local (NOT FOUND)
# 2. data.prod.svc.cluster.local (FOUND!)
# Result: Success
Scenario 2: Headless Service Resolution
# Normal Service - returns ClusterIP
$ nslookup my-nginx.default.svc.cluster.local
Name: my-nginx.default.svc.cluster.local
Address: 10.100.200.50
# Headless Service - returns all Pod IPs
$ nslookup my-nginx-headless.default.svc.cluster.local
Name: my-nginx-headless.default.svc.cluster.local
Address: 172.17.0.3
Name: my-nginx-headless.default.svc.cluster.local
Address: 172.17.0.4
Name: my-nginx-headless.default.svc.cluster.local
Address: 172.17.0.5
# Headless Service YAML:
# apiVersion: v1
# kind: Service
# metadata:
# name: my-nginx-headless
# spec:
# clusterIP: None # This makes it headless
# selector:
# app: nginx
Scenario 3: Pod DNS with Hostname/Subdomain
# Pod configurationapiVersion: v1
kind: Pod
metadata:name: web-1
spec:hostname: web-1
subdomain: web-service
...
# Headless Service (REQUIRED)apiVersion: v1
kind: Service
metadata:name: web-service
spec:clusterIP: None
...
# ✅ DNS Resolution
$ nslookup web-1.web-service.default.svc.cluster.local
Name: web-1.web-service.default.svc.cluster.local
Address: 172.17.0.10
# ❌ Without headless Service - DNS will fail
# Pod hostname/subdomain requires matching headless Service
Scenario 4: External DNS Resolution
# With ClusterFirst (default policy)
$ nslookup google.com
Server: 10.32.0.10
Address: 10.32.0.10#53
Non-authoritative answer:
Name: google.com
Address: 142.250.185.78
# Flow:
# 1. Query sent to CoreDNS (10.32.0.10)
# 2. CoreDNS sees it's not a cluster domain
# 3. CoreDNS forwards to upstream DNS (from node)
# 4. Response returned through CoreDNS to Pod# With Default policy - bypasses CoreDNS
$ nslookup google.com
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: google.com
Address: 142.250.185.78
# Flow:
# 1. Query sent directly to node's DNS (8.8.8.8)
# 2. No CoreDNS involvement
# 3. Cannot resolve Kubernetes Services
Best Practices for DNS Resolution
Use full FQDNs for cross-namespace access: Specify namespace explicitly to avoid ambiguity and reduce DNS query attempts.
Prefer short names within the same namespace: Simplifies configuration and leverages search domains efficiently.
Use headless Services for StatefulSets: Provides stable, predictable DNS names for each Pod.
Monitor CoreDNS performance: Watch for DNS resolution delays which can impact application latency.
Configure ndots appropriately: Lower ndots value (e.g., 2) can reduce DNS queries for external domains.
Common DNS Issues and Solutions
NXDOMAIN errors: Check namespace, service name spelling, and ensure Service exists.
Slow DNS resolution: High ndots value causes many search domain attempts. Consider using FQDNs with trailing dot (e.g., google.com.).
Cannot resolve Services with hostNetwork: Use dnsPolicy: ClusterFirstWithHostNet instead of default.
Custom DNS not working: Ensure dnsPolicy: "None" is set when using dnsConfig.
Pod hostname DNS fails: Verify matching headless Service exists with clusterIP: None.