Kubernetes
Kubernetes Service

Published: October 25, 2026 · 7 min read

Kubernetes Service Explained — ClusterIP to LoadBalancer

In the last class we made a Pod. We could not open it in a browser.

This class fixes that.

Here is the whole class in short:

  • Pods are short lived. They are deleted and recreated all the time.
  • Every new Pod gets a new IP address. The old one never comes back.
  • A Pod IP is also private. Nothing outside the cluster can reach it.
  • A Service is one fixed address in front of a changing set of Pods.
  • A Service finds Pods by label, not by name.
  • There are three types: ClusterIP, NodePort and LoadBalancer.
  • ClusterIP is the default — inside the cluster only.
  • NodePort opens a port on every node.
  • LoadBalancer asks the cloud for a public address.

Why a Kubernetes Service is needed — a pod IP changes when it restarts, a service name stays the same

The Problem

A Pod is not permanent. Kubernetes deletes and recreates Pods all the time:

  • the node dies
  • you deploy a new version
  • you scale down, then up again

Every new Pod gets a brand new IP. So you can never save a Pod IP anywhere. It will be wrong tomorrow.

And Pod IPs are private to the cluster. Even while the Pod is alive, a user cannot reach it.

Two problems, one answer: a Service.

How a Service Finds Pods

This is the part that confuses everyone, so look at it before the YAML.

How a Kubernetes Service finds its pods using a label selector

A Service does not know Pod names. It looks for Pods whose labels match its selector.

That is the whole connection. Your pod.yaml has a label. Your service.yaml has a selector. If they match, they are joined.

⚠️

Change the label on either side and the Service finds nothing. Your app returns an error while the Pods are perfectly healthy. This is the number one Service bug.

The Three Types

The three Kubernetes Service types compared — ClusterIP, NodePort and LoadBalancer

TypeWho can reach itUse it for
ClusterIPinside the cluster onlydatabases, internal APIs
NodePortanyone who can reach a nodetesting, clusters with no cloud
LoadBalancerthe public internetreal apps with real users

1. ClusterIP — the default

clusterip
# ClusterIP - the default, inside the cluster only

apiVersion: v1
kind: Service
metadata:
  name: hotel-app-svc
spec:
  type: ClusterIP        # you can leave this line out
  selector:
    app: hotel-app
  ports:
    - port: 80
      targetPort: 8080

# other pods reach it as:  http://hotel-app-svc

One internal address. Other Pods reach it by name. The outside world cannot.

Leave type out of your YAML and this is what you get.

2. NodePort — a port on every node

nodeport
# NodePort - opens a port on every node

apiVersion: v1
kind: Service
metadata:
  name: hotel-app-svc
spec:
  type: NodePort
  selector:
    app: hotel-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080     # optional, 30000-32767

# reach it at:  http://<any-node-ip>:30080

Kubernetes picks a port between 30000 and 32767 and opens it on every node. You reach the app at any node's IP plus that port.

It works anywhere, even on a laptop cluster. It is also ugly — nobody wants to visit a site on port 31472.

3. LoadBalancer — a real address

service.yaml
# service.yaml

apiVersion: v1
kind: Service
metadata:
  name: hotel-app-svc
spec:
  type: LoadBalancer
  selector:
    app: hotel-app
  ports:
    - port: 80
      targetPort: 8080
terminal
# create it

kubectl apply -f service.yaml
# service/hotel-app-svc created

kubectl get svc
# NAME            TYPE           CLUSTER-IP     EXTERNAL-IP                        PORT(S)
# hotel-app-svc   LoadBalancer   10.100.205.4   a8f291f...elb.amazonaws.com        80:30694/TCP
# kubernetes      ClusterIP      10.100.0.1     <none>                             443/TCP

The cloud gives you a public address in the EXTERNAL-IP column. Open it in a browser and your app is live.

They Stack On Each Other

How ClusterIP, NodePort and LoadBalancer stack on top of each other in Kubernetes

NodePort and LoadBalancer do not replace ClusterIP. They add to it.

Ask for a LoadBalancer and you get all three. A ClusterIP inside, a NodePort on every node, a public address on top.

That is why kubectl get svc showed 80:30694/TCP. The 80 is the Service port. The 30694 is the NodePort it opened underneath.

Which Type Do You Pick?

Ask one thing: who needs to reach this app?

  • Only other Pods need it. Use ClusterIP. A database, a cache, an internal API. Nothing outside the cluster should see it.
  • You need it right now, to test. Use NodePort. No cloud setup, no waiting. Open the node IP and the port.
  • Real users need it. Use LoadBalancer. One public address, and traffic spread across all your Pods.

Start with NodePort, because it works on every cluster. Move to LoadBalancer when real users need the app. Nobody is going to type a node IP and a five digit port into a browser.

One warning about LoadBalancer. Every LoadBalancer Service asks the cloud for its own load balancer. Ten such Services means ten load balancers, and ten line items on the bill. For one or two apps that is fine. Past that, teams put a single Ingress in front and leave every Service as ClusterIP.

port vs targetPort

Two ports in the YAML confuse people.

FieldMeaning
portthe port on the Service — what callers use
targetPortthe port your container listens on

Traffic arrives at port and Kubernetes forwards it to targetPort.

targetPort must match the containerPort in your Pod. If it does not, the connection goes nowhere.

How traffic reaches a Kubernetes pod through the load balancer, node port and target port

The Commands

terminal
# the commands you need

kubectl get svc                     # list services
kubectl get svc -o wide             # plus the selector
kubectl describe svc hotel-app-svc  # details and events
kubectl get endpoints hotel-app-svc # which pods it actually found

kubectl delete -f service.yaml      # delete it

kubectl get endpoints is the one to remember. It shows which Pods the Service actually found.

Empty list = your selector does not match your labels. That is your bug.

🚫

A LoadBalancer is a real cloud load balancer and it costs money every hour it exists. Delete it with kubectl delete -f service.yaml as soon as you no longer need it.

Kubernetes Service Quick Reference

CommandWhat it does
kubectl apply -f service.yamlcreate or update the Service
kubectl get svclist Services, types and EXTERNAL-IP
kubectl get svc -o widesame, plus the selector
kubectl describe svc <name>full details and events
kubectl get endpoints <name>which Pods it found
kubectl delete -f service.yamldelete it

What's Next

A Pod runs your app. A Service gives it an address that does not change, and decides who can reach it. The official Service documentation (opens in a new tab) and the Service types page (opens in a new tab) go deeper on each type.

← Back to Kubernetes Pod

Continue to Kubernetes Namespace →


Frequently Asked Questions

Why do Kubernetes pods need a Service?

Because pods are short lived. A pod is thrown away and recreated when a node fails, when you deploy, or when you scale. Every new pod gets a new IP address. Any address you saved is wrong the next day. A Service gives you one fixed name that always points at whichever pods are alive right now. Pod IPs are also private. A Service is how you open an app to real users.

What are the three types of Kubernetes Service?

ClusterIP, NodePort and LoadBalancer. ClusterIP is the default and is reachable only inside the cluster. NodePort opens a port between 30000 and 32767 on every node. You reach the app at a node IP plus that port. LoadBalancer asks your cloud provider for a real load balancer with a public address. They are not three separate things. NodePort includes a ClusterIP, and LoadBalancer includes both.

What is ClusterIP in Kubernetes?

ClusterIP is the default Service type. It gives your pods one internal IP and one DNS name that other pods can use. Nothing outside the cluster can reach it. Use it for anything internal, such as a database or an API only your own services call. If you do not write a type in your YAML, you get a ClusterIP.

What is the difference between NodePort and LoadBalancer?

NodePort opens the same port on every node, somewhere between 30000 and 32767. You reach the app at a node IP plus that port. It works on any cluster, including a laptop, and it is free. LoadBalancer goes further. It asks your cloud provider for a real load balancer with a public address. That spreads traffic across your nodes. LoadBalancer needs a cloud provider and it costs money while it exists.

Which Kubernetes Service type should I use?

Ask who needs to reach the app. If only other Pods need it, use ClusterIP. If you just want to test it quickly on any cluster, use NodePort. If real users need it, use LoadBalancer. Remember that each LoadBalancer Service creates its own cloud load balancer, so ten of them means ten bills.

What is the difference between port and targetPort in a Service?

port is the port on the Service itself, the one callers use. targetPort is the port your container is listening on inside the pod. Traffic arrives at port and Kubernetes forwards it to targetPort. They are often different. Users hit port 80 while the app inside listens on 8080. The targetPort must match the containerPort in your pod, or the connection goes nowhere.

My Service returns an error but the pods are running. Why?

Nine times out of ten the selector does not match the pod labels. A Service does not know pod names, it finds pods by label. If the service.yaml selector and the pod labels do not agree, the Service matches nothing. Run kubectl get endpoints on the Service. An empty list means it found no pods, so the labels are the problem. Also check that targetPort matches the containerPort.