πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Networking beginner ⏱ 10 minutes K8s 1.28+

Kubernetes Service Types Comparison

Compare Kubernetes Service types: ClusterIP for internal access, NodePort for direct port exposure, LoadBalancer for external traffic.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: `ClusterIP` (default) = internal-only access within the cluster. `NodePort` = exposes on every node’s IP at a static port (30000-32767). `LoadBalancer` = provisions an external cloud load balancer. `ExternalName` = DNS CNAME alias to an external service.

The Problem

You need to expose your application, but Kubernetes offers four Service types with different networking behaviors. Choosing wrong means either no external access when you need it, or unnecessary exposure when you don’t.

flowchart TB
    subgraph CLUSTER["Kubernetes Cluster"]
        CIP["ClusterIP<br/>10.96.0.1:80<br/>Internal only"]
        NP["NodePort<br/>NodeIP:30080<br/>All nodes"]
        LB["LoadBalancer<br/>External IP:80<br/>Cloud LB"]
        PODS["Backend Pods"]
    end
    
    INTERNAL["Internal Pod"] -->|"curl svc:80"| CIP
    CIP --> PODS
    
    EXTERNAL1["External Client"] -->|"curl NodeIP:30080"| NP
    NP --> PODS
    
    EXTERNAL2["External Client"] -->|"curl ExternalIP:80"| LB
    LB --> NP
    NP --> PODS

The Solution

ClusterIP (Default)

Internal-only access β€” pods within the cluster can reach this service:

apiVersion: v1
kind: Service
metadata:
  name: backend-api
spec:
  type: ClusterIP               # Default β€” can be omitted
  selector:
    app: backend
  ports:
    - port: 80                   # Service port
      targetPort: 8080           # Container port
# Accessible only from within the cluster
kubectl exec frontend-pod -- curl http://backend-api:80
kubectl exec frontend-pod -- curl http://backend-api.default.svc.cluster.local:80

NodePort

Exposes the service on a static port on every node:

apiVersion: v1
kind: Service
metadata:
  name: web-app
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80                   # ClusterIP port (internal)
      targetPort: 8080           # Container port
      nodePort: 30080            # External port (30000-32767)
# Accessible from outside on any node IP
curl http://192.168.1.10:30080   # worker-01
curl http://192.168.1.11:30080   # worker-02
curl http://192.168.1.12:30080   # worker-03 (even if no pods here)

LoadBalancer

Provisions an external load balancer (cloud or MetalLB):

apiVersion: v1
kind: Service
metadata:
  name: public-web
  annotations:
    # AWS-specific
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
kubectl get svc public-web
# NAME         TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)
# public-web   LoadBalancer   10.96.0.50    203.0.113.100    80:31234/TCP

# Accessible from internet
curl http://203.0.113.100

ExternalName

DNS alias to an external service (no proxying):

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.example.com   # DNS CNAME
# Resolves to db.example.com
kubectl exec app -- nslookup external-db
# external-db.default.svc.cluster.local β†’ db.example.com

Comparison Table

FeatureClusterIPNodePortLoadBalancerExternalName
Internal accessβœ…βœ…βœ…βœ… (DNS only)
External accessβŒβœ… (node IP)βœ… (LB IP)N/A
Port rangeAny30000-32767AnyN/A
Load balancingkube-proxykube-proxyCloud LBNone
CostFreeFreeCloud LB feeFree
Use caseMicroservicesDev/testingProductionExternal DB

Headless Service (ClusterIP: None)

Returns pod IPs directly β€” no load balancing:

apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None               # Headless β€” no virtual IP
  selector:
    app: postgres
  ports:
    - port: 5432
# Returns individual pod IPs
kubectl exec app -- nslookup db-headless
# db-headless.default.svc.cluster.local β†’ 10.244.1.5, 10.244.2.8, 10.244.3.12

# Used by StatefulSets for stable network identity
# postgres-0.db-headless.default.svc.cluster.local β†’ 10.244.1.5

Common Patterns

# Internal microservice β†’ ClusterIP
type: ClusterIP

# Development access β†’ NodePort
type: NodePort

# Production web app β†’ LoadBalancer (or Ingress + ClusterIP)
type: LoadBalancer

# Database in another VPC β†’ ExternalName
type: ExternalName

# StatefulSet discovery β†’ Headless
clusterIP: None

Common Issues

IssueCauseFix
LoadBalancer stuck in ``No cloud LB provisionerInstall MetalLB for bare-metal
NodePort connection refusedFirewall blocking 30000-32767Open port range in firewall/security group
Service not resolvingWrong selector labelsVerify `kubectl get endpoints svc-name` shows IPs
External traffic not reaching pods`externalTrafficPolicy: Cluster` (default)Set to `Local` to preserve source IP
ClusterIP not accessible from outsideBy design β€” internal onlyUse NodePort, LoadBalancer, or Ingress

Best Practices

  • Default to ClusterIP β€” expose externally only when needed
  • Use Ingress/Gateway API instead of NodePort β€” better routing, TLS, virtual hosts
  • Use `externalTrafficPolicy: Local` for LoadBalancer β€” preserves client IP
  • Avoid NodePort in production β€” limited port range, no DNS, no TLS
  • Use headless for StatefulSets β€” enables direct pod-to-pod communication
  • Annotate LoadBalancer services β€” cloud-specific settings (NLB vs ALB, internal vs external)

Key Takeaways

  • ClusterIP = internal only (default, most common)
  • NodePort = all nodes listen on port 30000-32767 (dev/testing)
  • LoadBalancer = cloud LB with external IP (production)
  • ExternalName = DNS CNAME to external service (no proxy)
  • Headless (clusterIP: None) = returns pod IPs directly (StatefulSets)
  • In production, prefer Ingress/Gateway API + ClusterIP over direct LoadBalancer per service
#services #clusterip #nodeport #loadbalancer #networking
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens