Every Pod gets its own IP address when it starts. But Pods are temporary — they crash, get replaced, get rescheduled to different nodes. Every time a new Pod starts, it gets a completely different IP. If your frontend is hardcoded to talk to 10.244.1.5 and that Pod dies, the new Pod at 10.244.2.3 is unreachable. Your app breaks.
A Service solves this by giving you one stable, permanent address that never changes — no matter how many times the Pods behind it are replaced.
Without Service:
Frontend -> 10.244.1.5 (Pod IP) -> Pod dies -> new Pod is 10.244.2.3 -> BROKEN
With Service:
Frontend -> 10.96.45.12 (Service IP) -> always works
Service tracks healthy Pods using labels
Pod dies and is replaced -> Service finds the new Pod automatically
Frontend never notices anything changed
The Service IP never changes. It is registered in the cluster's internal DNS so you can also reach it by name:
<http://backend-service> (within same namespace)
<http://backend-service.default.svc.cluster.local> (full DNS name)
A Service does not track specific Pods by name or IP. It uses label selectors — it finds every Pod in the cluster that has a matching label and routes traffic to them.
# The Service says: "route traffic to anything with label app: backend"
spec:
selector:
app: backend
# The Pod must have that label to be picked up
metadata:
labels:
app: backend <- Service finds this Pod
This is powerful — when a Pod crashes and a new one is created with the same label, the Service automatically starts routing to the new Pod. You never update the Service. Labels do the wiring.
You can verify which Pod IPs the Service is actually routing to at any moment:
kubectl get endpoints backend-service
# Shows the live Pod IPs currently behind this Service
If a Pod is unhealthy or not matching the label, it does not appear in endpoints — the Service only routes to ready, matching Pods.
ClusterIP is the default Service type. It gives you a stable internal IP that only works inside the cluster. External traffic cannot reach it at all.
Frontend Pod -> ClusterIP Service -> Backend Pods
(internal cluster IP)
Works: Pod A calls backend-service:80 -> reaches backend pods
Fails: Someone outside the cluster tries to reach it -> no route
Use ClusterIP for anything that should not be publicly exposed — databases, internal APIs, cache layers, backend services that only your own Pods need to talk to.
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP # default — you can omit this line entirely
selector:
app: backend
ports:
- port: 80 # port the Service listens on inside the cluster
targetPort: 8080 # port the container actually runs on