| 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:
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: AlwaysApply the manifest and wait until the backend and client are ready:
kubectl apply -f dns-lab.yamlkubectl rollout status deployment/web -n dns-lab --timeout=120skubectl wait pod/dns-client -n dns-lab --for=condition=Ready --timeout=120sThe 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:
kubectl exec -n dns-lab dns-client -- nslookup webSample output:
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: web.dns-lab.svc.cluster.local
Address: 10.96.110.120The 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:
kubectl exec -n dns-lab dns-client -- nslookup web.dns-labkubectl exec -n dns-lab dns-client -- nslookup web.dns-lab.svc.cluster.local.Sample output:
Name: web.dns-lab.svc.cluster.local
Address: 10.96.110.120The 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:
kubectl exec -n dns-lab dns-client -- getent hosts web.dns-lab.svc.cluster.localSample output:
10.96.110.120 web.dns-lab.svc.cluster.localThe usual Service FQDN shape is:
<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:
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: AlwaysApply the manifest and wait for the API Deployment and client Pod:
kubectl apply -f dns-cross-namespace.yamlkubectl rollout status deployment/api -n backend --timeout=120skubectl wait pod/dns-client-frontend -n frontend --for=condition=Ready --timeout=120sKubernetes 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:
kubectl exec -n frontend dns-client-frontend -- nslookup api.backendSample output:
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: api.backend.svc.cluster.local
Address: 10.107.75.54A 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:
kubectl exec -n frontend dns-client-frontend -- dig api.frontend.svc.cluster.local. +noall +answer +commentsSample output:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 54865
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1There 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:
kubectl exec -n frontend dns-client-frontend -- cat /etc/resolv.confSample output:
search frontend.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5Confirm the backend Service exists in the other namespace:
kubectl get service -n backend apiFix. Point the application at the namespace-qualified or fully qualified name:
kubectl exec -n frontend dns-client-frontend -- nslookup api.backendkubectl exec -n frontend dns-client-frontend -- dig api.backend.svc.cluster.local. +shortSample output:
10.107.75.54Update 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:
kubectl exec -n dns-lab dns-client -- cat /etc/resolv.confSample output:
search dns-lab.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5The 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:
kubectl exec -n frontend dns-client-frontend -- cat /etc/resolv.confSample output:
search frontend.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5The 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:
kubectl exec -n dns-lab dns-client -- dig +search web +noall +answerSample output:
web.dns-lab.svc.cluster.local. 30 IN A 10.96.110.120dig 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:
kubectl exec -n dns-lab dns-client -- dig example.com. +shortCheck 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:
kubectl get pod -n dns-lab dns-client -o jsonpath='{.spec.dnsPolicy}{"\n"}'Sample output:
ClusterFirstClusterFirst 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:
kubectl get service -n dns-lab webSample output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.96.110.120 <none> 80/TCP 5mList EndpointSlices for the same name:
kubectl get endpointslice -n dns-lab -l kubernetes.io/service-name=webSample output:
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
web-xxxxx IPv4 8080 192.168.5.23,192.168.5.36 5mClusterIP 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:
kubectl exec -n dns-lab dns-client -- dig web-headless.dns-lab.svc.cluster.local. +noall +answerSample 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.23When 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:
kubectl get pods -n kube-system -l k8s-app=kube-dns -o wideSample 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-cpInspect the Service front end:
kubectl get service kube-dns -n kube-systemSample output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 2d9hList DNS endpoint addresses:
kubectl get endpointslice -n kube-system -l kubernetes.io/service-name=kube-dnsSample output:
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
kube-dns-bmrj2 IPv4 53,53,9153 192.168.62.184,192.168.62.129 2d9hConfirm each endpoint is ready:
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:
192.168.62.184 ready=true
192.168.62.129 ready=trueEndpointSlices 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:
kubectl logs -n kube-system -l k8s-app=kube-dns --prefix --tail=20Sample 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 timeoutLook 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:
kubectl get configmap coredns -n kube-system -o yamlSample 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:
kubernetesserves in-cluster Service and Pod records.forwardsends other queries to upstream resolvers.cachestores responses for zones where caching remains enabled. In this lab Corefile, success and denial caching are disabled forcluster.local, so cluster-local records are not cached by this plugin configuration.loopdetects forwarding loops.errorsand plugin logs surface processing failures.reloadapplies 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:
- Exact spelling and case-normalized DNS name
- Service namespace
- Service existence (
kubectl get service) - Correct cluster domain from
resolv.confor the Corefilekuberneteszone - Whether a short name expanded in the expected namespace
- Headless Service EndpointSlices when you expect Pod A records
- Search path and
ndotsside effects - 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:
kubectl exec -n dns-lab dns-client -- dig web.dns-lab.svc.cluster.local. +shortkubectl exec -n dns-lab dns-client -- dig missing.dns-lab.svc.cluster.local. +noall +answer +commentsSample output:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 27120
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1NXDOMAIN 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:
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +shortSample output:
10.96.0.1Troubleshoot 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.
kubectl get deployment coredns -n kube-system -o jsonpath='{.spec.template.spec.serviceAccountName}{"\n"}'Sample output:
corednskubectl get clusterrole system:coredns -o yamlkubectl get clusterrolebinding system:coredns -o yamlThe 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:
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +shortkubectl exec -n dns-lab dns-client -- dig example.com +noall +answer +commentsWhen 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:
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +shortkubectl exec -n dns-lab dns-client -- dig example.com +noall +answer +commentsSample output for the external query:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 41203Diagnose. Read CoreDNS logs for upstream timeouts or unreachable resolvers:
kubectl logs -n kube-system -l k8s-app=kube-dns --prefix --tail=20Inspect 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:
kubectl exec -n dns-lab dns-client -- dig example.com. +shortIf 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:
- Inspect the
forwardtarget in the Corefile (/etc/resolv.confinside CoreDNS Pods on many kubeadm clusters). - Compare node
/etc/resolv.confwith what CoreDNS uses for upstream queries. - Test the upstream resolver from a debug Pod with
dig @<upstream-ip> example.comwhen direct access is available. - Compare node and Pod resolution for the same public name.
- Look for a local stub resolver or forwarding loop (
systemd-resolvedstub files are one possible node-level cause, not the default diagnosis). - 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-dnsClusterIP 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-dnson 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:
kubectl exec -n dns-lab dns-client -- dig kubernetes.default.svc.cluster.local. +shortkubectl exec -n frontend dns-client-frontend -- dig api.backend.svc.cluster.local. +shortSample output:
10.96.0.1
10.107.75.54Matching internal answers from both namespaces confirm cluster DNS records while you continue investigating a single misconfigured Pod.
Final DNS decision flow
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 loopWhat's Next
- Kubernetes NetworkPolicy with Examples
- Kubernetes Ingress Rules with Host, Path and TLS Examples
- Kubernetes Gateway API with HTTPRoute Examples
References
- Kubernetes documentation — DNS for Services and Pods
- Kubernetes documentation — Debugging DNS resolution
- CoreDNS documentation
- Kubernetes API reference — Pod DNS config
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.

