Cluster DNS and Service Discovery
A Pod reported NXDOMAIN for the apiserver and then fetched a page from it seconds later. This guide takes that apart, because the wrong conclusion - that the Pod is isolated from the API - would have been a security finding that was not true.
Cloud Native Security Guide 5 of 42 Beginner
- Kubernetesapiserver v1.36.4, kubelet v1.36.3
- Runtimecontainerd 2.2.6
- CNICilium 1.18.1 - tunnel/VXLAN, with Hubble relay and UI
- Host OSUbuntu 26.04 LTS, kernel 7.0.0-29
- Built withkubeadm v1.36.3 - podSubnet 10.244.0.0/16, serviceSubnet 10.96.0.0/12
- TimeAbout 12 min
- Reviewed25 August 2026
Written against the versions above. CoreDNS at 10.96.0.10 with the standard kubeadm search list and `ndots:5`. busybox 1.37's `nslookup` is the tool that misleads here; it behaves this way on every Kubernetes cluster.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA5001 | 192.168.0.41 | Ubuntu 26.04 LTS | Control Plane Node (tainted NoSchedule) | 2 Core | 4 GB | 50 GB |
| CKA5001-NODE01 | 192.168.0.42 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA5001-NODE02 | 192.168.0.43 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA5001-NODE03 | 192.168.0.44 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
Before you start
- guide 4 - the cluster boundary this tests.
-
The failure, exactly as it appeared
One name, the short one everybody uses.
bash Example session kubectl run kcsa-dns --image=docker.io/library/busybox:1.37 --restart=Never --command -- sh -c 'sleep 300' >/dev/null && kubectl wait --for=condition=Ready pod/kcsa-dns --timeout=120s >/dev/null && kubectl exec kcsa-dns -- nslookup kubernetes.default.svc 2>&1 | grep -vE '^$' | head -4; echo "--- NXDOMAIN, and a non-zero exit"Server: 10.96.0.10Address: 10.96.0.10:53** server can't find kubernetes.default.svc: NXDOMAIN** server can't find kubernetes.default.svc: NXDOMAIN--- NXDOMAIN, and a non-zero exitExpected result
** server can't find kubernetes.default.svc: NXDOMAINandcommand terminated with exit code 1.Success conditionYou have the failure in front of you.
-
The same name, fully qualified
Add the suffix the search list would have added.
bash Example session kubectl exec kcsa-dns -- nslookup kubernetes.default.svc.cluster.local 2>&1 | grep -vE '^$' | tail -3; echo "--- the SAME name fully qualified resolves, so DNS was never broken"Address: 10.96.0.10:53Name: kubernetes.default.svc.cluster.localAddress: 10.96.0.1--- the SAME name fully qualified resolves, so DNS was never brokenkubectl exec kcsa-dns -- cat /etc/resolv.conf; echo "--- the search list nslookup ignored and every other tool uses"search default.svc.cluster.local svc.cluster.local cluster.local practicelabpro.localnameserver 10.96.0.10options ndots:5--- the search list nslookup ignored and every other tool usesExpected result
Name: kubernetes.default.svc.cluster.local,Address: 10.96.0.1. Then asearchlist of four domains,nameserver 10.96.0.10andoptions ndots:5.Success conditionYou know the resolver configuration is correct.
-
The tool that gets it right, on the same Pod
Nothing changed except which binary asked.
bash Example session kubectl exec kcsa-dns -- sh -c 'wget -q -O- --no-check-certificate --timeout=5 https://kubernetes.default.svc/version 2>&1 | head -c 130'; echo; echo "--- wget applies the search list, reaches the apiserver by SHORT name, and gets a real answer"{ "major": "1", "minor": "36", "emulationMajor": "1", "emulationMinor": "36", "minCompatibilityMajor": "1", "minCompat--- wget applies the search list, reaches the apiserver by SHORT name, and gets a real answerkubectl delete pod kcsa-dns --wait=true 2>&1 | tail -1; kubectl get pod kcsa-dns 2>&1 | tail -1pod "kcsa-dns" deleted from default namespaceError from server (NotFound): pods "kcsa-dns" not foundExpected resultThe apiserver's
/versionpayload -"major": "1","minor": "36"- fetched by the SHORT name. Then the Pod is deleted.Success conditionYou can tell a resolver problem from a tool problem.
Troubleshooting
busybox
nslookupreturns NXDOMAIN for a Service that exists.Why: It does not apply the
searchlist from/etc/resolv.conf.Fix:Use the fully qualified name, or test with
wget,curlorgetent hosts.External DNS lookups are slow inside Pods.
Why:
ndots:5sends short names through every search domain first.Fix:Use a trailing dot for absolute names, or set a lower
ndotsindnsConfig.A Pod cannot resolve anything at all.
Why: CoreDNS is down, or a NetworkPolicy blocks egress to it on port 53.
Fix:
kubectl -n kube-system get pods -l k8s-app=kube-dns, then check egress policy.Concluded a Pod is isolated from the apiserver because DNS failed.
Why: The tool, not the network.
Fix:Confirm with a second tool before recording it. This page is that mistake.