Finding things by name
Cluster DNS, simply.
Cluster DNS lets apps find each other by name instead of memorizing IPs. DNS (Domain Name System) turns a name into the address a computer dials. A cluster is the group of machines Kubernetes runs your apps on.
Pods get replaced, and each replacement gets a new IP (for example 10.96.45.12). If the frontend hard-coded a pod IP, it would break on the next swap. Point apps at a Service: a stable name and address in front of those Pods. CoreDNS, the cluster DNS server, resolves that name to the Service IP.
The names follow a pattern. Say a Service named db lives in namespace data. A namespace is a named slice of the cluster that keeps one team's objects apart from another's. From the same namespace, the short name is db. From another namespace, use db.data. The full name is db.data.svc.cluster.local. All three point at the same Service; you will type the short one most days.
Set up a Service to find
Let's make this real instead of theoretical. The namespace called data doesn't exist yet, and you can't drop a Service into a namespace that isn't there, so you make it first. A namespace, remember, is that named area that keeps one team's things apart from another's.
kubectl create namespace data
namespace/data created
Now you need a Service worth looking up. Below is a complete file you can save and apply exactly as it is. It creates a Service named db in the data namespace that sends traffic to any Pod wearing the label app: db. A label is just a small key-value tag you stick on Pods so a Service knows which ones belong to it. You won't create any Pods with that label here, and that's fine. A Service gets its name and address the moment it exists, so DNS can answer for it even before a single Pod shows up.
apiVersion: v1kind: Servicemetadata:name: dbnamespace: dataspec:selector:app: dbports:- port: 5432targetPort: 5432
kubectl apply -f db-service.yaml
service/db created
Now ask the cluster for that Service's current address. kubectl is the command-line tool you use to talk to the cluster, and the -n flag tells it which namespace to look in.
kubectl get svc db -n data
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEdb ClusterIP 10.96.45.12 <none> 5432/TCP 12s
That CLUSTER-IP, 10.96.45.12, is the number a caller would otherwise have to know. You won't type it anywhere. The name db carries it for you, and the number is free to change without you ever noticing.
Look the name up
You can watch DNS do its job with one command. This spins up a tiny throwaway Pod, runs a name lookup inside the cluster, and deletes itself the moment it finishes. Because you don't pass -n, the Pod lands in the default namespace, so you'll reach db by its cross-namespace name, db.data.
kubectl run dnstest --rm -it --image=busybox:1.36 --restart=Never -- nslookup db.data
Server: 10.96.0.10Address: 10.96.0.10:53Name: db.data.svc.cluster.localAddress: 10.96.45.12pod "dnstest" deleted
The lookup came back with the full name db.data.svc.cluster.local and the address 10.96.45.12, the exact number you saw a moment ago. That Server line, 10.96.0.10, is CoreDNS itself answering. In your app's config, the database host is simply db.data. No IP addresses live in your files, and there is nothing to edit when Pods come and go underneath.
Now watch it fail
Beginners hit this one constantly, so it's worth seeing on purpose. The throwaway Pod is in the default namespace, not data. Ask it for the bare short name db, and there is nothing for CoreDNS to match, because a short name only means something inside the Service's own namespace.
kubectl run dnstest --rm -it --image=busybox:1.36 --restart=Never -- nslookup db
Server: 10.96.0.10Address: 10.96.0.10:53*** Can't find db: No answerpod "dnstest" deletedpod default/dnstest terminated (Error)
Read what it's telling you. CoreDNS answered on the first two lines, so DNS is healthy; it simply had no record for db from where you asked. The last line, terminated (Error), is just nslookup exiting non-zero because the lookup came up empty, not a cluster fault. The fix is the one you already ran: add the namespace and ask for db.data (or the full db.data.svc.cluster.local), and the answer comes straight back. This single mistake, a short name used from the wrong namespace, is behind a large share of 'my app can't reach the database' tickets.
Writing your config against names instead of numbers is what makes an app easy to move and hard to break. Pod addresses shift constantly, and even a Service's address can change if it gets recreated, but the names stay put. Your config keeps working while the cluster reshuffles beneath it. The same file that says db.data runs unchanged in your test setup, in staging, and in production, because the naming pattern is identical in every cluster. And because almost everything finds its dependencies through DNS, a broken cluster DNS makes many apps fail at once and can look like a giant mysterious outage when it's really one thing. Seasoned operators check DNS first whenever a pile of unrelated things breaks together, and you'll practice that in the troubleshooting lessons.
Cluster DNS (CoreDNS) lets pods find Services by name: short names inside a namespace, FQDNs across namespaces. Prefer DNS names in config maps over IPs. When DNS breaks, everything looks like an app outage.
Debug with nslookup from a busybox pod toward service.namespace.svc.cluster.local. If DNS fails but ClusterIP curl works, you have a DNS/path problem instead of a Service selector problem.
In production, watch CoreDNS replicas and latency. A starved CoreDNS is a cluster-wide incident waiting to happen.
Try this
From a debug pod, resolve a Service short name and FQDN, then wget the Service to prove name → VIP → pod.
$ kubectl create deployment svcname --image=nginx:1.27 --replicas=1deployment.apps/svcname created$ kubectl expose deployment svcname --port=80service/svcname exposed$ kubectl run dnsbox --image=busybox:1.36 --restart=Never --command -- sleep 300pod/dnsbox created$ kubectl exec dnsbox -- nslookup svcnameServer: 10.96.0.10Name: svcname.default.svc.cluster.localAddress: 10.96.88.21$ kubectl exec dnsbox -- nslookup svcname.default.svc.cluster.localName: svcname.default.svc.cluster.localAddress: 10.96.88.21$ kubectl exec dnsbox -- wget -qO- http://svcname | Select-String nginx | Select-Object -First 1Welcome to nginx!$ kubectl delete pod dnsbox; kubectl delete svc svcname; kubectl delete deployment svcnamepod "dnsbox" deletedservice "svcname" deleteddeployment.apps "svcname" deleted
Takeaway
Use Service DNS names, not pod IPs. Verify with nslookup from a debug pod, and treat CoreDNS health as tier-0 infrastructure for the whole cluster.