Skip to content
C Codeloom
DevOps

Kubernetes Services Explained: ClusterIP, NodePort, LoadBalancer

A clear guide to Kubernetes Services. Learn what ClusterIP, NodePort, LoadBalancer, and headless Services do, and when to use each in real clusters.

·4 min read · By Codeloom
Beginner 10 min read

What you'll learn

  • Why pods need Services in front of them
  • What ClusterIP, NodePort, and LoadBalancer actually do
  • When to use a headless Service
  • How selectors and endpoints connect Services to pods
  • How a Service differs from an Ingress

Prerequisites

  • Comfortable with the Linux command line

Pods come and go. Their IP addresses change with them. If your frontend hardcoded a backend pod IP, the first restart would break everything. Kubernetes solves this with the Service abstraction: a stable virtual endpoint that load-balances traffic across a moving set of pods. Once you understand Services, most of Kubernetes networking falls into place.

The selector model

A Service does not own pods. It watches for pods that match a label selector and forwards traffic to them. That is the whole trick.

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

Any pod labeled app: web becomes a backend for this Service automatically. Add a pod, remove a pod, replace them with new versions — the Service keeps routing to whoever is ready right now.

Under the hood, the cluster maintains an Endpoints (or EndpointSlice) object that lists the actual pod IPs. You can see it with:

kubectl get endpoints web

That output is the ground truth of where traffic actually goes.

ClusterIP: the default

ClusterIP is the default Service type. It allocates a stable virtual IP reachable only from inside the cluster. Other pods talk to the Service by name through cluster DNS — web.default.svc.cluster.local, or just web if they are in the same namespace.

ClusterIP is what you want for almost everything that is not user-facing: internal APIs, databases, caches, message brokers. It keeps your traffic on a private wire and gives you a name that does not change.

NodePort: open a port on every node

NodePort extends ClusterIP by also opening the same port on every node in the cluster. Hit <any-node-ip>:30080 and you reach the Service.

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080

NodePort is useful for quick demos, on-prem clusters without a cloud load balancer, and bare-metal setups behind an external load balancer you manage yourself. In a managed cloud cluster, you rarely use it directly — LoadBalancer is friendlier.

LoadBalancer: cloud-provisioned

On a managed Kubernetes cluster (EKS, GKE, AKS), setting type: LoadBalancer tells the cloud controller to provision a real external load balancer, point it at the NodePort, and assign it a public IP or DNS name.

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

One Service per public endpoint is fine for a single app. If you have dozens of services that all need TLS and hostname routing, you usually put one LoadBalancer in front of an Ingress controller and let Ingress rules fan traffic out.

Headless Services

Setting clusterIP: None makes a Service headless. Instead of a virtual IP, DNS returns the actual pod IPs as A records. This is the right tool when clients need to address specific pods by name — most commonly with StatefulSets like databases or Kafka brokers.

apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None
  selector:
    app: db
  ports:
    - port: 5432

Combined with a StatefulSet, each pod becomes addressable at a predictable name like db-0.db-headless.

ExternalName: a DNS alias

ExternalName is the odd one out. It does not select pods at all — it just returns a CNAME to an external hostname. Useful when you want code inside the cluster to point at payments.svc.cluster.local while it actually resolves to a third-party API. Swap the target later without changing application code.

Services versus Ingress

A Service exposes one workload on one port pair. An Ingress routes HTTP and HTTPS based on host and path across many Services. They are not alternatives; they layer. A typical production setup uses ClusterIP Services for every workload, then a single LoadBalancer Service in front of an Ingress controller that holds the public IP.

Debugging tips

A few commands cover most networking problems.

kubectl get svc
kubectl describe svc web
kubectl get endpoints web
kubectl run debug --rm -it --image=alpine -- sh

From inside the debug pod, wget -qO- http://web confirms cluster DNS and the Service IP work. If endpoints is empty, your selector does not match any ready pods — fix the labels or the readiness probe.

Services are deliberately boring once you understand them, and that is the point. They give your workloads stable names and stable load balancing while everything else underneath rolls forward.