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.

How Services Work

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)

How Services Find Pods — Label Selectors

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.

Four Types of Services

ClusterIP — Internal Only (Default)

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

NodePort — Expose on Every Node