🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

What is Service Discovery in Kubernetes? Explained Simply (2026)

How do pods find each other in Kubernetes? Service discovery is the mechanism that lets services communicate without hardcoded IPs. Here's how it works, simply explained.

DevOpsBoysMay 25, 20264 min read
Share:Tweet

In Kubernetes, pods get new IP addresses every time they restart. If your frontend pod hardcoded the backend's IP address, it would break every time the backend restarted. Service discovery solves this — it gives services stable names so pods can find each other without knowing IPs.


The Problem Service Discovery Solves

Let's say you have a frontend app talking to a backend API. Without service discovery:

Frontend pod (IP: 10.0.0.15) → Backend pod (IP: 10.0.0.22)

Backend pod restarts → new IP: 10.0.0.47 → frontend can't find it.

With service discovery:

Frontend pod → backend.default.svc.cluster.local → (finds current backend pods)

The name stays the same. The IP lookup always returns current, healthy pod IPs.


How Kubernetes Service Discovery Works

Kubernetes uses two mechanisms:

  1. Services — a stable virtual IP (ClusterIP) and DNS name for a group of pods
  2. CoreDNS — a DNS server running inside the cluster that resolves service names to ClusterIPs

Together, they give every service a stable DNS name that works from any pod.


Step 1: Services (The Stable Endpoint)

A Service in Kubernetes is a load balancer in front of your pods. It selects pods using labels.

yaml
apiVersion: v1
kind: Service
metadata:
  name: backend            # Service name
  namespace: default
spec:
  selector:
    app: backend           # Select pods with this label
  ports:
  - port: 80               # Port the service listens on
    targetPort: 8080       # Port the pods listen on

What this creates:

  • A ClusterIP (virtual IP like 10.96.0.45) — stable, doesn't change
  • A DNS name: backend.default.svc.cluster.local

Even if backend pods restart with new IPs, the Service's ClusterIP and DNS name remain the same.


Step 2: CoreDNS (The DNS Resolver)

CoreDNS is a DNS server that runs as a deployment in the kube-system namespace. Every pod in the cluster is configured to use CoreDNS as its DNS server.

bash
# Check CoreDNS pods
kubectl get pods -n kube-system | grep coredns
 
# Check what DNS server pods use
kubectl exec -it mypod -- cat /etc/resolv.conf

Output:

nameserver 10.96.0.10    # CoreDNS ClusterIP
search default.svc.cluster.local svc.cluster.local cluster.local

When your frontend pod does curl http://backend/api, CoreDNS resolves backend10.96.0.45 (the Service ClusterIP) → forwards to a backend pod.


DNS Name Format

Kubernetes services get predictable DNS names:

<service-name>.<namespace>.svc.cluster.local

Examples:

  • backend.default.svc.cluster.local — full name
  • backend.default — works within same cluster
  • backend — works within same namespace

Short names work because of the search domains in /etc/resolv.conf.

So from a pod in the default namespace, all of these resolve to the same service:

bash
curl http://backend/api
curl http://backend.default/api
curl http://backend.default.svc.cluster.local/api

From a pod in a different namespace (e.g., frontend namespace), you need:

bash
curl http://backend.default/api       # Must specify namespace

Types of Services

ClusterIP (default)

Only accessible inside the cluster. Used for pod-to-pod communication.

yaml
spec:
  type: ClusterIP   # Default — no external access

NodePort

Exposes the service on a port of every node. External traffic can reach it via <NodeIP>:<NodePort>.

yaml
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080    # External port (30000–32767)

LoadBalancer

Provisions a cloud load balancer (AWS ALB, GCP Load Balancer) that routes to pods.

yaml
spec:
  type: LoadBalancer   # Creates cloud LB, gets external IP

Headless Service

Returns pod IPs directly instead of a ClusterIP. Used for stateful apps (databases) where you want to connect to a specific pod.

yaml
spec:
  clusterIP: None     # No virtual IP — returns pod IPs directly

Real Example: Frontend Calling Backend

yaml
# Backend deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend        # Label must match Service selector
    spec:
      containers:
      - name: backend
        image: myapp/backend:latest
        ports:
        - containerPort: 8080
---
# Backend service
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  selector:
    app: backend
  ports:
  - port: 80
    targetPort: 8080

Frontend code:

javascript
// No hardcoded IPs — use service name
const response = await fetch('http://backend/api/users')

This works because:

  1. backend resolves via CoreDNS to the Service ClusterIP
  2. kube-proxy routes the request to one of the backend pods
  3. Pod restarts? Service stays, DNS stays, traffic continues

How Load Balancing Works

When a Service has multiple pod replicas, kube-proxy load balances traffic across them using iptables or IPVS rules on each node.

bash
# See 3 backend pods
kubectl get pods -l app=backend -o wide
NAME            IP
backend-abc12   10.0.1.5
backend-def45   10.0.1.6
backend-ghi78   10.0.1.7
 
# Service routes to all three (round-robin by default)

Debugging Service Discovery

bash
# Test DNS resolution from inside a pod
kubectl run debug --image=busybox --rm -it -- sh
nslookup backend
nslookup backend.default.svc.cluster.local
 
# Check if service exists and has endpoints
kubectl get service backend
kubectl get endpoints backend
 
# No endpoints = no matching pods (check labels)
kubectl get pods --show-labels | grep backend
 
# Check if pods match service selector
kubectl describe service backend | grep Selector

Common issue: Service has no endpoints.

bash
# Endpoints empty = selector labels don't match pod labels
kubectl get endpoints backend
# NAME      ENDPOINTS   AGE
# backend   <none>      5m  ← Problem: no pods match selector

Fix: verify your pod labels match the Service's spec.selector.


Summary

ConceptWhat it does
ServiceStable virtual IP + DNS name in front of pods
CoreDNSDNS server inside the cluster — resolves service names
ClusterIPVirtual IP that stays constant while pods change
DNS name<service>.<namespace>.svc.cluster.local
kube-proxyRoutes traffic from Service IP to pod IPs

Service discovery in Kubernetes is automatic — create a Service and any pod in the cluster can reach it by name.

Related: Kubernetes Network Policies Blocking Traffic Fix | Kubernetes Architecture Explained | CoreDNS Resolution Failures Fix

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments