Kubernetes DNS Troubleshooting: Services, CoreDNS and Name Resolution

Tested on Rocky Linux 10.2 (Red Quartz) workstation
Package kubectl 1.36.3
Applies to Any host with kubectl configured; Kubernetes 1.27+ cluster with Linux worker nodes
Cert prep CKAD · CKA · CKS
Lab environment Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm
Privilege Normal user (no sudo required on the workstation)
Scope Service DNS and Pod resolver checks; resolv.conf, ndots, and dnsPolicy; CoreDNS Pods, logs, and Corefile; NXDOMAIN, SERVFAIL, and timeout classification; upstream forwarding; and a decision flow. Does not cover CoreDNS installation, NodeLocal DNSCache, DNS autoscaling, ExternalDNS, public zone management, or full Service traffic troubleshooting beyond DNS.
Related guides Kubernetes Services

Name resolution failures in Kubernetes are rarely “DNS is broken everywhere.” More often the queried name, namespace, Pod resolver settings, Service record, CoreDNS health, or upstream forward path is wrong for that specific query. DNS records sit on top of cluster networking—see Kubernetes networking for CNI, Service IPs, and kube-proxy before you chase only CoreDNS logs. This guide is split into two parts: Pod-level Service DNS checks (the CKAD slice) and shared CoreDNS or upstream troubleshooting (CKAD and CKA).


Quick reference

Run lookups from a dedicated client Pod so every check uses the same resolver path. Replace NS and SVC with your namespace and Service name.

Step Command What to check
1 kubectl exec -n NS dns-client -- getent hosts kubernetes.default.svc.cluster.local Cluster DNS infrastructure — if this fails, skip Service-name checks
2 kubectl exec -n NS dns-client -- cat /etc/resolv.conf search domains and ndots before you blame CoreDNS
3 kubectl exec -n NS dns-client -- dig SVC.NS.svc.cluster.local. +short Service record exists in the queried namespace
4 kubectl get svc,endpointslices -n kube-system -l k8s-app=kube-dns kube-dns ClusterIP and CoreDNS backends when step 1 fails
5 kubectl get pods -n kube-system -l k8s-app=kube-dns and kubectl logs -n kube-system -l k8s-app=kube-dns CoreDNS readiness and plugin or forward errors
6 kubectl exec -n NS dns-client -- dig example.com +short Upstream forwarding when in-cluster names work but public names fail

DNS success does not prove healthy Service backends—pair lookups with EndpointSlice checks from Service troubleshooting. See final DNS decision flow for the full branch tree.


Part 1 — CKAD slice: Service DNS and name resolution

Part 1 stays at the Pod and Service layer. You verify names from a dedicated client, read resolver configuration, and confirm that DNS answers match the Service and EndpointSlices you expect.

Start with a dedicated DNS test Pod

Application images often omit DNS tools. Keep a long-lived debug Pod with nslookup, dig, and getent so every check uses the same resolver path:

Tool Best use
nslookup Simple interactive DNS checks
dig Response codes, server selection, and query detail
getent hosts Resolution through the container libc resolver

Save the following as dns-lab.yaml:

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: dns-lab
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: dns-lab
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: app
        image: registry.k8s.io/e2e-test-images/agnhost:2.39
        args: ["serve-hostname", "--http=true", "--port=8080"]
        ports:
        - name: http
          containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: dns-lab
spec:
  selector:
    app: web
  ports:
  - name: http
    port: 80
    targetPort: http
---
apiVersion: v1
kind: Service
metadata:
  name: web-headless
  namespace: dns-lab
spec:
  clusterIP: None
  selector:
    app: web
  ports:
  - name: http
    port: 80
    targetPort: http
---
apiVersion: v1
kind: Pod
metadata:
  name: dns-client
  namespace: dns-lab
spec:
  containers:
  - name: tools
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

Apply the manifest and wait until the backend and client are ready:

bash
kubectl apply -f dns-lab.yaml
bash
kubectl rollout status deployment/web -n dns-lab --timeout=120s
bash
kubectl wait pod/dns-client -n dns-lab --for=condition=Ready --timeout=120s

The Pod stays running so you can repeat lookups without recreating it.

Resolve a Service in the same namespace

From dns-client in dns-lab, test the short Service name:

bash
kubectl exec -n dns-lab dns-client -- nslookup web

Sample output:

output
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	web.dns-lab.svc.cluster.local
Address: 10.96.110.120

The unqualified name web expanded through the Pod search path to web.dns-lab.svc.cluster.local and returned the ClusterIP.

Test the namespace-qualified and fully qualified forms:

bash
kubectl exec -n dns-lab dns-client -- nslookup web.dns-lab
bash
kubectl exec -n dns-lab dns-client -- nslookup web.dns-lab.svc.cluster.local.

Sample output:

output
Name:	web.dns-lab.svc.cluster.local
Address: 10.96.110.120

The trailing dot on the last query marks an absolute name so the resolver does not append search domains.

Confirm the same answer through the libc resolver path:

bash
kubectl exec -n dns-lab dns-client -- getent hosts web.dns-lab.svc.cluster.local

Sample output:

output
10.96.110.120   web.dns-lab.svc.cluster.local

The usual Service FQDN shape is:

text
<service>.<namespace>.svc.<cluster-domain>

cluster.local is common but not universal. Confirm the actual cluster domain from /etc/resolv.conf search lines or the CoreDNS kubernetes plugin zone before you assume cluster.local.

The remaining lab commands use cluster.local because that is the domain configured on this kubeadm cluster. Replace it with the domain shown by your Pod search path or CoreDNS kubernetes zone when your cluster uses another suffix. The cluster suffix is configured by the kubelet and may differ between installations.

Resolve a Service across namespaces

Save the following as dns-cross-namespace.yaml:

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: backend
---
apiVersion: v1
kind: Namespace
metadata:
  name: frontend
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: app
        image: registry.k8s.io/e2e-test-images/agnhost:2.39
        args: ["serve-hostname", "--http=true", "--port=8080"]
        ports:
        - name: http
          containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: api
  namespace: backend
spec:
  selector:
    app: api
  ports:
  - port: 80
    targetPort: http
---
apiVersion: v1
kind: Pod
metadata:
  name: dns-client-frontend
  namespace: frontend
spec:
  containers:
  - name: tools
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

Apply the manifest and wait for the API Deployment and client Pod:

bash
kubectl apply -f dns-cross-namespace.yaml
bash
kubectl rollout status deployment/api -n backend --timeout=120s
bash
kubectl wait pod/dns-client-frontend -n frontend --for=condition=Ready --timeout=120s

Kubernetes short-name resolution depends on the querying Pod's namespace, so the client Pod must exist and be running before the cross-namespace test.

Query from frontend Expected behaviour
api Searches the client Pod namespace (frontend) first
api.backend Targets the backend namespace
api.backend.svc.cluster.local. Complete Service name with absolute termination

Resolve the cross-namespace name:

bash
kubectl exec -n frontend dns-client-frontend -- nslookup api.backend

Sample output:

output
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	api.backend.svc.cluster.local
Address: 10.107.75.54

A short Service name is namespace-relative. To reach a Service in another namespace, include the namespace label or use the full cluster DNS name.

Walk through a cross-namespace DNS failure

This lab reproduces a common misconfiguration: an application in frontend is given the short hostname api, but the Service lives in backend.

Symptom. The frontend client cannot resolve the backend API:

bash
kubectl exec -n frontend dns-client-frontend -- dig api.frontend.svc.cluster.local. +noall +answer +comments

Sample output:

output
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 54865
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

There is no api Service in frontend. The short name api is expanded through the Pod search path and never reaches backend unless you qualify it.

Diagnose. Read the resolver search list:

bash
kubectl exec -n frontend dns-client-frontend -- cat /etc/resolv.conf

Sample output:

output
search frontend.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

Confirm the backend Service exists in the other namespace:

bash
kubectl get service -n backend api

Fix. Point the application at the namespace-qualified or fully qualified name:

bash
kubectl exec -n frontend dns-client-frontend -- nslookup api.backend
bash
kubectl exec -n frontend dns-client-frontend -- dig api.backend.svc.cluster.local. +short

Sample output:

output
10.107.75.54

Update the application configuration to use api.backend or api.backend.svc.cluster.local instead of the bare api. No CoreDNS change is required—the record exists; the query used the wrong namespace scope.

Inspect the Pod /etc/resolv.conf

The kubelet writes cluster DNS settings into each Pod. Read them from the client:

bash
kubectl exec -n dns-lab dns-client -- cat /etc/resolv.conf

Sample output:

output
search dns-lab.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

The nameserver line normally points at the cluster DNS Service ClusterIP (kube-dns in kube-system). The search line supplies suffixes the resolver tries for relative names. Your cluster may include additional search domains beyond svc.cluster.local and cluster.local; treat the file as the source of truth for expansion behaviour.

Compare a Pod in another namespace:

bash
kubectl exec -n frontend dns-client-frontend -- cat /etc/resolv.conf

Sample output:

output
search frontend.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

The namespace-specific search entry changes with the Pod namespace, which is why the same short name can resolve differently in dns-lab versus frontend.

Understand ndots and search expansion

Names with fewer dots than the configured ndots value are normally tried against search domains first. A complete FQDN with a trailing dot is treated as absolute and skips search expansion.

Query a short in-namespace name with dig +search to see the effective answer:

bash
kubectl exec -n dns-lab dns-client -- dig +search web +noall +answer

Sample output:

output
web.dns-lab.svc.cluster.local. 30 IN	A	10.96.110.120

dig resolved web to web.dns-lab.svc.cluster.local using the search list.

High ndots plus multiple search domains can issue several queries before an external name succeeds. Changing ndots alters application lookup behaviour and is not a universal performance fix; confirm internal names still resolve after any dnsConfig change.

For stable external lookups, prefer an absolute name with a trailing dot:

bash
kubectl exec -n dns-lab dns-client -- dig example.com. +short

Check Pod dnsPolicy

dnsPolicy controls which resolver settings the kubelet applies:

Policy Behaviour
ClusterFirst Uses cluster DNS for cluster names and forwards other queries upstream
Default Inherits node resolver settings selected by the kubelet
ClusterFirstWithHostNet Preserves cluster DNS for hostNetwork Pods
None Requires custom settings through dnsConfig

Confirm the default on the test Pod:

bash
kubectl get pod -n dns-lab dns-client -o jsonpath='{.spec.dnsPolicy}{"\n"}'

Sample output:

output
ClusterFirst

ClusterFirst is the default when dnsPolicy is omitted. A hostNetwork Pod that still needs cluster Service names should use ClusterFirstWithHostNet. Custom dnsConfig can add nameservers, search domains, and resolver options; keep that secondary to fixing the cluster DNS path first.

Verify the Service and EndpointSlices

When DNS returns an address but the application cannot connect, confirm the Service and backends separately. DNS success does not prove ready endpoints exist.

Inspect the Service:

bash
kubectl get service -n dns-lab web

Sample output:

output
NAME   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
web    ClusterIP   10.96.110.120   <none>        80/TCP    5m

List EndpointSlices for the same name:

bash
kubectl get endpointslice -n dns-lab -l kubernetes.io/service-name=web

Sample output:

output
NAME        ADDRESSTYPE   PORTS   ENDPOINTS                  AGE
web-xxxxx   IPv4          8080    192.168.5.23,192.168.5.36   5m

ClusterIP Services normally resolve even when they have no ready backends. A headless Service has no virtual ClusterIP. With a selector, its DNS A or AAAA records normally return the ready endpoint addresses represented through EndpointSlices. publishNotReadyAddresses: true can also publish endpoints that are not Ready. Headless Services resolve to their backend endpoint addresses rather than one Service virtual IP:

bash
kubectl exec -n dns-lab dns-client -- dig web-headless.dns-lab.svc.cluster.local. +noall +answer

Sample output:

output
web-headless.dns-lab.svc.cluster.local.	30 IN A	192.168.5.36
web-headless.dns-lab.svc.cluster.local.	30 IN A	192.168.5.23

When DNS is correct but connections fail, continue with troubleshoot a Service that is not working rather than changing CoreDNS first.


Part 2 — Shared troubleshooting: CoreDNS and upstream resolution

Part 2 covers cluster DNS infrastructure: classifying the failure symptom, checking CoreDNS, reading logs and the Corefile, and separating in-cluster record problems from upstream forwarding or network timeouts.

Classify the DNS failure first

Start from the error shape, not from a random CoreDNS restart:

Result Main direction
NXDOMAIN Name does not exist from the DNS server perspective
SERVFAIL DNS server could not complete the query
Timeout or no servers reached Network path, DNS Service, policy, or CoreDNS availability
Cluster names work, public names fail Upstream forwarding or node resolver
Public names work, Service names fail Kubernetes DNS records, CoreDNS permissions, or wrong namespace or name
Only one Pod fails Pod dnsPolicy, dnsConfig, search path, or application resolver
Only Pods on one node fail Node resolver, CNI, or node-local DNS path

Not every NXDOMAIN means CoreDNS is down. A mistyped Service name returns NXDOMAIN while kubernetes.default still resolves.

Check CoreDNS Pods, Service, and EndpointSlices

CoreDNS commonly runs behind the legacy kube-dns Service name:

bash
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide

Sample output:

output
NAME                       READY   STATUS    RESTARTS   AGE    IP               NODE
coredns-589f44dc88-7q8t5   1/1     Running   9          2d9h   192.168.62.184   k8s-cp
coredns-589f44dc88-xcrkp   1/1     Running   0          13h    192.168.62.129   k8s-cp

Inspect the Service front end:

bash
kubectl get service kube-dns -n kube-system

Sample output:

output
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   2d9h

List DNS endpoint addresses:

bash
kubectl get endpointslice -n kube-system -l kubernetes.io/service-name=kube-dns

Sample output:

output
NAME             ADDRESSTYPE   PORTS        ENDPOINTS                       AGE
kube-dns-bmrj2   IPv4          53,53,9153   192.168.62.184,192.168.62.129   2d9h

Confirm each endpoint is ready:

bash
kubectl get endpointslice -n kube-system -l kubernetes.io/service-name=kube-dns -o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{" ready="}{.conditions.ready}{"\n"}{end}'

Sample output:

output
192.168.62.184 ready=true
192.168.62.129 ready=true

EndpointSlices explicitly track ready, serving, and terminating conditions; listing addresses alone is not the same as confirming ready DNS backends.

Compare the kube-dns ClusterIP with the nameserver line in a failing Pod resolv.conf. Without NodeLocal DNSCache or another custom DNS setup, a mismatch between the Pod nameserver and the kube-dns ClusterIP suggests custom dnsConfig or kubelet clusterDNS configuration. With NodeLocal DNSCache, the Pod intentionally uses a local DNS address instead of the kube-dns ClusterIP.

Read CoreDNS logs

Pull recent logs from all CoreDNS replicas:

bash
kubectl logs -n kube-system -l k8s-app=kube-dns --prefix --tail=20

Sample output:

output
[pod/coredns-589f44dc88-xcrkp/coredns] [ERROR] plugin/errors: 2 _grpc_config.example.svc. TXT: read udp 192.168.62.129:57923->192.168.0.1:53: i/o timeout

Look for plugin startup failures, Corefile parse errors, forwarding timeouts, loop detection, permission errors, repeated SERVFAIL, and upstream i/o timeout lines.

If you need per-query detail temporarily, enable the CoreDNS log plugin in the Corefile, reproduce the failure, then remove the plugin and roll back the ConfigMap. Do not leave verbose query logging enabled in production without a retention plan.

For deeper event context around the CoreDNS Deployment, see kubectl logs, events, and describe.

Inspect the CoreDNS Corefile

Read the live ConfigMap:

bash
kubectl get configmap coredns -n kube-system -o yaml

Sample output:

output
data:
  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 {
           disable success cluster.local
           disable denial cluster.local
        }
        loop
        reload
        loadbalance
    }

Relevant directives for troubleshooting:

  • kubernetes serves in-cluster Service and Pod records.
  • forward sends other queries to upstream resolvers.
  • cache stores responses for zones where caching remains enabled. In this lab Corefile, success and denial caching are disabled for cluster.local, so cluster-local records are not cached by this plugin configuration.
  • loop detects forwarding loops.
  • errors and plugin logs surface processing failures.
  • reload applies Corefile changes without manual Pod edits.

This article does not teach full CoreDNS plugin syntax. Change the Corefile only with a rollback plan.

Troubleshoot NXDOMAIN

Work through name and record checks before touching CoreDNS replicas:

  1. Exact spelling and case-normalized DNS name
  2. Service namespace
  3. Service existence (kubectl get service)
  4. Correct cluster domain from resolv.conf or the Corefile kubernetes zone
  5. Whether a short name expanded in the expected namespace
  6. Headless Service EndpointSlices when you expect Pod A records
  7. Search path and ndots side effects
  8. Negative caching after a recently created record, but only when denial caching is enabled for the queried zone

Compare short, namespace-qualified, and absolute queries:

bash
kubectl exec -n dns-lab dns-client -- dig web.dns-lab.svc.cluster.local. +short
bash
kubectl exec -n dns-lab dns-client -- dig missing.dns-lab.svc.cluster.local. +noall +answer +comments

Sample output:

output
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 27120
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

NXDOMAIN with the aa (authoritative) flag from cluster DNS usually means the Kubernetes record truly does not exist for that name.

Confirm the baseline internal record still works:

bash
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +short

Sample output:

output
10.96.0.1

Troubleshoot SERVFAIL

SERVFAIL means the server received the query but could not produce an answer. Check CoreDNS logs, Corefile syntax, and whether CoreDNS can reach upstream resolvers.

Confirm which ServiceAccount the CoreDNS Deployment uses, then inspect the ClusterRole and binding that grant it API access. On kubeadm clusters, the ServiceAccount is commonly kube-system/coredns, and the associated ClusterRole is commonly named system:coredns.

bash
kubectl get deployment coredns -n kube-system -o jsonpath='{.spec.template.spec.serviceAccountName}{"\n"}'

Sample output:

output
coredns
bash
kubectl get clusterrole system:coredns -o yaml
bash
kubectl get clusterrolebinding system:coredns -o yaml

The rules should cover the resources required by the deployed Corefile, normally including services, endpointslices, namespaces, pods, and legacy endpoints with list and watch. The official DNS troubleshooting guide checks these permissions because missing watches can cause internal queries to return SERVFAIL.

Compare in-cluster and external resolution from the same client:

bash
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +short
bash
kubectl exec -n dns-lab dns-client -- dig example.com +noall +answer +comments

When internal names succeed and external names return SERVFAIL, focus on the forward directive and upstream resolver health rather than Service selectors.

Walk through an upstream forwarding failure

This lab reproduces the split symptom: cluster Service names resolve, but public names fail.

Symptom. Internal baseline works, external lookup does not:

bash
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +short
bash
kubectl exec -n dns-lab dns-client -- dig example.com +noall +answer +comments

Sample output for the external query:

output
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 41203

Diagnose. Read CoreDNS logs for upstream timeouts or unreachable resolvers:

bash
kubectl logs -n kube-system -l k8s-app=kube-dns --prefix --tail=20

Inspect the forward target in the Corefile (/etc/resolv.conf inside CoreDNS Pods on many kubeadm clusters). Compare node /etc/resolv.conf with what CoreDNS uses for upstream queries.

Fix. Restore reachability to a working upstream resolver—correct node DNS settings, fix firewall rules on UDP and TCP port 53, or update the Corefile forward directive to a known-good resolver. Roll the CoreDNS ConfigMap only with a rollback plan, then confirm:

bash
kubectl exec -n dns-lab dns-client -- dig example.com. +short

If external names still fail while kubernetes.default resolves, the problem remains in the forward path—not in Service selectors or Pod search domains.

Troubleshoot upstream DNS forwarding

When Service names work but public names fail:

  1. Inspect the forward target in the Corefile (/etc/resolv.conf inside CoreDNS Pods on many kubeadm clusters).
  2. Compare node /etc/resolv.conf with what CoreDNS uses for upstream queries.
  3. Test the upstream resolver from a debug Pod with dig @<upstream-ip> example.com when direct access is available.
  4. Compare node and Pod resolution for the same public name.
  5. Look for a local stub resolver or forwarding loop (systemd-resolved stub files are one possible node-level cause, not the default diagnosis).
  6. Check firewall and network reachability to upstream DNS on UDP and TCP port 53.

Troubleshoot DNS timeouts

When queries hang or time out instead of returning NXDOMAIN or SERVFAIL:

  • Confirm reachability to the kube-dns ClusterIP from the client Pod namespace
  • Check CoreDNS Pod readiness and restart counts
  • Test both UDP and TCP port 53 (large responses may require TCP)
  • Review NetworkPolicy egress to kube-dns on port 53 — see Kubernetes NetworkPolicy when policy is in play
  • Look for CNI or node-specific failures when only one node's Pods are affected
  • Watch for overloaded or repeatedly restarting CoreDNS replicas
  • Measure upstream resolver latency when only external names time out

Compare DNS from different locations

Run the same query from multiple places to isolate the failure layer:

  • Two Pods in the same namespace
  • Pods in different namespaces
  • Pods scheduled on different nodes
  • A debug Pod versus an application Pod with custom dnsConfig

Use kubectl debug Pods when you need an ephemeral tools container beside a failing workload.

Example baseline from the lab client:

bash
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +short
bash
kubectl exec -n frontend dns-client-frontend -- dig api.backend.svc.cluster.local. +short

Sample output:

output
10.96.0.1
10.107.75.54

Matching internal answers from both namespaces confirm cluster DNS records while you continue investigating a single misconfigured Pod.

Final DNS decision flow

text
Does kubernetes.default resolve?
├─ No
│  ├─ Check Pod resolv.conf
│  ├─ Check kube-dns Service and EndpointSlices
│  ├─ Check CoreDNS Pods and logs
│  └─ Check NetworkPolicy and CNI path
└─ Yes
   ├─ Target Service name fails
   │  ├─ Check namespace and FQDN
   │  ├─ Check Service existence
   │  └─ Check headless Service EndpointSlices
   └─ Public name fails
      ├─ Check CoreDNS forward directive
      ├─ Check upstream resolver
      └─ Check timeout, firewall, or resolver loop

What's Next


References

Summary

You split Kubernetes DNS troubleshooting into Pod-level Service checks and cluster-wide CoreDNS investigation. From a dedicated dns-client Pod you verified short names, namespace-qualified names, and absolute FQDNs, then read /etc/resolv.conf to see how search domains and ndots expand queries before they reach cluster DNS.

The key distinction is between “the name is wrong or missing” and “DNS infrastructure cannot answer.” NXDOMAIN on a mistyped Service or wrong namespace scope is a record problem; kubernetes.default failing points at kube-dns, CoreDNS readiness, or Pod resolver settings. DNS can succeed while traffic still fails when EndpointSlices list no ready backends, so keep Service and EndpointSlice checks in the same workflow as name lookups.

When internal names work but public names return SERVFAIL or time out, inspect the Corefile forward directive and upstream resolver reachability before you change application dnsPolicy. Use the decision flow to pick the next branch, and hand off connection failures to the Service troubleshooting guide once names resolve to the expected addresses.


Frequently Asked Questions

1. What is the fully qualified DNS name for a Kubernetes Service?

The usual form is service.namespace.svc.cluster.domain where cluster.domain defaults to cluster.local on many clusters. Confirm the cluster domain from Pod resolv.conf search lines or the CoreDNS kubernetes plugin zone before assuming cluster.local.

2. Why does nslookup work for a short Service name but fail for the long form?

An unqualified short name is expanded using search domains in resolv.conf. A long name without a trailing dot may still be treated as relative and have extra suffixes appended when ndots rules apply, which can produce unexpected NXDOMAIN or SERVFAIL results.

3. Does a successful DNS lookup prove the Service has healthy backends?

No. ClusterIP Services normally resolve to the Service virtual IP even when EndpointSlices list no ready endpoints. DNS success only proves the name exists in cluster DNS records.

4. What is the first cluster-wide check when no Pod can resolve internal names?

Verify the kube-dns Service ClusterIP, CoreDNS Pod readiness, and kube-dns EndpointSlices in kube-system. Then read CoreDNS logs for plugin or forwarding errors before changing Pod dnsPolicy on individual workloads.

5. When should I use dnsPolicy ClusterFirstWithHostNet?

Use it for hostNetwork Pods that must still resolve Kubernetes Service names through cluster DNS. ClusterFirst alone does not configure cluster DNS the same way for hostNetwork Pods.

6. Why do external names fail while kubernetes.default resolves?

CoreDNS answers in-cluster names through the kubernetes plugin and forwards everything else upstream according to the forward directive in the Corefile. Broken upstream resolvers, forwarding loops, or firewall blocks on port 53 commonly cause that split symptom.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)