Finding things by name
Cluster DNS, simply.
Your phone doesn't ask you to memorize numbers. You pick a name out of your contacts, tap it, and the call goes through. If that person switches to a new number, you update it in one place and everything keeps working. A Kubernetes cluster hands your apps the same convenience. A cluster is just the group of machines Kubernetes runs your apps across, and the feature that lets those apps find each other by name is called cluster DNS. DNS is short for Domain Name System: the service that turns a friendly name into the numeric address a computer actually dials.
Pods don't stay put, and that's the whole reason names beat numbers. Your app runs inside Pods, and a Pod is the smallest thing Kubernetes runs, roughly one live copy of your app. Pods get replaced all the time, and every fresh one comes up with a brand-new IP address, the numeric network address like 10.96.45.12 that machines use to reach each other. If your frontend had memorized a Pod's number, it would break the second that Pod was swapped out. So you don't point apps at Pods. You point them at a Service, which is a steady name and address parked in front of a group of Pods. You reach the Service by its name, and that name never changes. CoreDNS, a small program Kubernetes runs as the cluster's always-current phone book, quietly turns each name into the right address for you.
The names follow a pattern that's easy to guess once you've seen it. Say you have a Service named db living in a namespace called data. A namespace is just a named area inside the cluster that keeps one team's things separate from another's, a bit like folders on a shared drive. From an app in that same namespace, you call the Service by its short name: db. From an app in a different namespace, you tack the namespace on the end: db.data. The full, spelled-out version is db.data.svc.cluster.local. All three names point at the same Service, and you'll 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.