🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

What is a Kubernetes Service? ClusterIP, NodePort, LoadBalancer Explained Simply

Pods can die and restart with new IPs. A Service gives them a stable address. Here's how ClusterIP, NodePort, and LoadBalancer actually work — with clear examples.

DevOpsBoysMay 13, 20264 min read
Share:Tweet

A pod dies. Kubernetes starts a new one. The new pod gets a different IP address. Any other pod that was talking to the old IP address now gets connection refused.

This is why Kubernetes Services exist.


What a Service Does

A Service is a stable network endpoint that sits in front of one or more pods. It has:

  • A fixed ClusterIP that never changes
  • A DNS name (auto-created)
  • A selector that defines which pods it routes to

When pods die and restart with new IPs, the Service automatically updates its routing. Everything talking to the Service keeps working.

┌─────────────┐          ┌─────────────────────┐
│ Other Pod   │ ─────→  │ Service (stable IP) │ ─────→ Pods
│ (10.244.1.5)│          │ (10.96.45.100)      │        (any IP)
└─────────────┘          └─────────────────────┘

The Four Service Types

1. ClusterIP (Default)

Accessible only inside the cluster. Not reachable from outside.

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-api
spec:
  selector:
    app: my-api
  ports:
    - port: 80          # Port the Service listens on
      targetPort: 8080  # Port the pod listens on
  type: ClusterIP       # Default — can omit this line

DNS name: my-api.default.svc.cluster.local
Short form: my-api (within same namespace)

bash
# From another pod inside the cluster:
curl http://my-api/health
curl http://my-api.default.svc.cluster.local/health
 
# From outside the cluster: NOT accessible

Use when: Internal service-to-service communication. Database services. Internal APIs.


2. NodePort

Opens a specific port on every node in the cluster. Traffic to <NodeIP>:<NodePort> reaches your pods.

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-api
spec:
  selector:
    app: my-api
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080   # Port on the actual node (30000–32767)
  type: NodePort
bash
# Accessible from outside using any node's IP:
curl http://192.168.1.10:30080    # Node 1
curl http://192.168.1.11:30080    # Node 2 — same result

Use when: Quick testing on local clusters (kind, minikube). Simple external access without a cloud load balancer.

Don't use in production — exposes ports on every node, hard to manage at scale.


3. LoadBalancer

Provisions an actual cloud load balancer (AWS ALB/NLB, GCP Load Balancer, Azure LB). Gets an external IP.

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-api
spec:
  selector:
    app: my-api
  ports:
    - port: 80
      targetPort: 8080
  type: LoadBalancer
bash
kubectl get svc my-api
# NAME     TYPE           CLUSTER-IP     EXTERNAL-IP          PORT(S)
# my-api   LoadBalancer   10.96.45.100   34.100.200.150:80    80:30080/TCP
 
# External IP is your cloud load balancer's IP
curl http://34.100.200.150/health

Use when: You need external access in a cloud environment. Each LoadBalancer service creates one cloud LB (expensive at scale).

For multiple services: Use Ingress instead of multiple LoadBalancer services.


4. ExternalName

Maps a Service to an external DNS name. No proxying — just DNS CNAME.

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-database
spec:
  type: ExternalName
  externalName: mydb.rds.amazonaws.com

Pods inside the cluster can now reach my-database and it resolves to your RDS endpoint.

Use when: Abstracting external services so pods don't need to hardcode external hostnames.


How Services Find Pods: Selectors and Endpoints

Services use label selectors to find pods:

yaml
# Service selector
spec:
  selector:
    app: my-api
    environment: production
 
# Pod must have matching labels
metadata:
  labels:
    app: my-api
    environment: production
bash
# See which pods a service is routing to
kubectl get endpoints my-api -n <namespace>
# NAME     ENDPOINTS                         AGE
# my-api   10.244.1.5:8080,10.244.2.3:8080   5m
 
# If ENDPOINTS shows <none> → labels don't match

The most common mistake: typo in the selector label. Always check endpoints when a service isn't routing.


Service DNS — How Pods Find Each Other

Every Service gets a DNS entry automatically via CoreDNS:

<service-name>.<namespace>.svc.cluster.local

Examples:

bash
# From a pod, reaching the "payments" service in "billing" namespace:
curl http://payments.billing.svc.cluster.local/api/charge
 
# Short form (same namespace only):
curl http://payments/api/charge
 
# Environment variables also injected (older method):
# PAYMENTS_SERVICE_HOST=10.96.100.50
# PAYMENTS_SERVICE_PORT=80

ClusterIP vs NodePort vs LoadBalancer — Decision Table

ScenarioService Type
Pod-to-pod internal trafficClusterIP
Testing on local Kubernetes (kind/minikube)NodePort
Production external access (cloud)LoadBalancer + Ingress
Abstracting external DB/serviceExternalName
Multiple HTTP services behind one IPIngress (not Service type)

Quick Debugging

bash
# Service not routing? Check endpoints:
kubectl get endpoints <service-name>
 
# DNS not resolving? Test from a pod:
kubectl exec -it <any-pod> -- nslookup <service-name>
 
# See service details:
kubectl describe svc <service-name>
 
# Port mismatch? Check targetPort matches container port:
kubectl get pod <pod> -o yaml | grep containerPort

Services are one of the most fundamental Kubernetes concepts. Once you understand ClusterIP vs NodePort vs LoadBalancer, everything else in networking (Ingress, Gateway API) builds on top of this foundation.

For hands-on Kubernetes networking labs, KodeKloud has CKA-prep courses that cover Services, Endpoints, DNS, and Ingress in depth.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments