← Back

2026

Kubernetes Service Types: NodePort vs ClusterIP vs LoadBalancer

A practical guide to the three core Service types. What they do, when to use each and how they layer on top of each other.

When you deploy an application in Kubernetes, the Pods that run your workload are ephemeral. They come and go, get rescheduled across nodes and receive new IP addresses each time. A Service is the Kubernetes abstraction that gives you a stable network endpoint to reach those Pods, though not all Services work the same way.

Type
Accessible from
Use case
ClusterIP
Inside the cluster only
Internal microservice communication
NodePort
Outside via node IP + port
Dev/testing, simple external access
LoadBalancer
Outside via a cloud load balancer
Production external traffic

ELI5

Imagine your Kubernetes cluster is an apartment building. Pods are people living in individual apartments. They move around and their room numbers change. Services are the building's reception desk: a stable address that forwards to the right person.

🏢ClusterIP

The reception desk is inside the building.

Only residents (other Pods) can walk up to it. Guests from outside cannot get in.

🚪NodePort

A special side door on every floor.

Anyone who knows the building's address AND the floor number can knock on that door and be connected to the right resident.

🤵LoadBalancer

A professional doorman out front.

Guests only need the official building address. The doorman figures out which door and floor to send them to.

ClusterIP

Internal cluster traffic only.

Inside cluster
Outside cluster
Pod inside the cluster
Service (ClusterIP)
Target Pods

The default Service type. Gives your Service a stable virtual IP that only other Pods inside the cluster can reach. kube-proxy on each node maintains iptables rules that DNAT traffic to one of the backing Pods.

apiVersion: v1
kind: Service
metadata:
name: my-service

The Service name.

spec:
type: ClusterIP

The default type, so it can be omitted entirely.

selector:
app: my-app

Matches Pods with this label.

ports:
- port: 80

The port the Service exposes inside the cluster.

targetPort: 8080

The port the container actually listens on.

NodePort

External traffic via any node's IP on a static port.

Inside cluster
Outside cluster
External client → node-ip:30080
Service (NodePort)
Target Pods

Opens a port on every node in the cluster. Any external client that knows a node IP and the port can reach the Service. A NodePort Service always gets a ClusterIP too, it builds on top of it.

Note

The caller must know a node IP (not production-friendly). If the hit node doesn't run a target Pod, kube-proxy adds an extra network hop. Use externalTrafficPolicy: Local to avoid this, but then only nodes with a running Pod accept traffic.

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort

Opens a port on every node in the cluster.

selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080

Optional. If omitted, Kubernetes auto-assigns a value in the 30000–32767 range.

LoadBalancer

External traffic via a cloud-provisioned public IP.

Inside cluster
Outside cluster
External client
Cloud LB (stable public IP)
Service (LoadBalancer)
Target Pods

Provisions a cloud load balancer in front of your cluster. The LB gets a stable public IP and distributes traffic to the NodePort on your nodes. Kubernetes automatically creates a NodePort and ClusterIP underneath.

Tip

In production, use one LoadBalancer Service pointing to an Ingress Controller (nginx-ingress, Traefik) rather than a separate LB per app. One cloud LB instead of many, with significant cost savings.

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: LoadBalancer

Provisions a cloud load balancer automatically.

selector:
app: my-app
ports:
- port: 80

Public-facing port on the cloud LB.

targetPort: 8080

Container port. Kubernetes handles the NodePort underneath.

How they relate to each other

A key insight: every Service type builds on the previous one. When you create a LoadBalancer Service, Kubernetes also automatically allocates a NodePort and a ClusterIP. The cloud provider's load balancer then forwards traffic to the node ports.

LoadBalancer

└─►NodePort(always created underneath)

└─►ClusterIP(always created underneath)

└─►kube-proxy / iptables rules

Deep dive: traffic flow

ClusterIP

Pod A

DNS lookup: my-service → 10.96.0.1

kube-proxy

Intercepts via iptables/IPVS, DNAT to a backing Pod IP

Target Pod

Response travels back directly

NodePort

External client

<node-ip>:30080

kube-proxy

Forwards to the ClusterIP (extra hop if no local Pod)

Target Pod

Standard ClusterIP routing from here

If the chosen node doesn't run a target Pod, kube-proxy forwards to another node, adding an extra network hop. Avoid this with externalTrafficPolicy: Local, but then only nodes with a target Pod accept traffic.

LoadBalancer

External client

http://203.0.113.10:80

Cloud LB

Distributes across registered node IP + NodePort combos

Kubernetes NodePort

Handles the rest from here

When to use what

ClusterIP
  • Services only need to talk to each other inside the cluster
  • You expose a database, cache, or internal API
  • You do not want the service reachable from outside at all
NodePort
  • Running locally (minikube or kind) without a cloud LB
  • Quick external access for testing or demos
  • Managing your own external load balancer (HAProxy, nginx) outside Kubernetes
LoadBalancer
  • Running on a managed cloud (AKS, EKS, GKE, etc.)
  • Need a stable public IP with health checks
  • Exposing a production service to end users

LoadBalancer without a cloud provider

A common misconception is that type: LoadBalancer only works on public clouds. In reality, it just requires something that watches for LoadBalancer Services and provisions an IP in response. On bare-metal or on-prem clusters, you can install a controller that does exactly that.

Popular on-prem solutions

Solution
Protocol
Notes
MetalLB
ARP (L2) / BGP (L3)
Most popular. Free, open source, battle-tested.
kube-vip
ARP / BGP
Lightweight. Runs as a static pod or DaemonSet. Also handles control-plane VIPs.
OpenELB
ARP / BGP
CNCF sandbox project by KubeSphere.
PureLB
Linux networking / BGP
Uses standard Linux networking primitives.
F5 / Citrix / HAProxy Enterprise
Various
Commercial hardware or virtual appliances with Kubernetes controllers.

How MetalLB works

In a cloud environment, the cloud controller manager watches for type: LoadBalancer Services and calls the cloud API to provision a load balancer. On bare-metal there is no cloud API, so the Service stays in Pending state forever, unless you install something like MetalLB.

Configure an IP pool

e.g. 192.168.1.200–192.168.1.250

Service is created

MetalLB assigns an IP from the pool to the LoadBalancer Service.

IP is announced

Via ARP (Layer 2 mode) or to upstream routers via BGP (Layer 3 mode).

Traffic arrives

Reaches a node and follows normal NodePort → ClusterIP routing.

Summary by environment

AKS / EKS / GKE

Cloud controller manager → cloud LB

Bare-metal / on-prem

MetalLB, kube-vip, or similar → virtual IP via ARP/BGP

Private cloud (OpenStack, vSphere)

Cloud provider integration or MetalLB

Quick comparison

Feature
ClusterIP
NodePort
LoadBalancer
Reachable inside cluster
Reachable outside cluster
Requires cloud provider
✗ (MetalLB for on-prem)
Stable public IP
Port range restriction
30000–32767
Extra cost
None
None
Cloud LB cost
Production-ready
✓ internal
⚠ limited

Summary

ClusterIPDefault and safest option for internal communication.
NodePortPunches a hole through every node's firewall, which is useful in dev but noisy in production.
LoadBalancerCloud-native way to expose services externally with a dedicated IP.

Understanding these three types is essential before diving into more advanced topics like Ingress, Gateway API, or Service Meshes. All of which ultimately rely on these building blocks.