Service Types
Understand every Kubernetes Service type and when to use each one.
Time: ~15 minutes Difficulty: Intermediate
What You Will Learn
Section titled “What You Will Learn”- ClusterIP: internal-only access (default)
- NodePort: expose on a static port on every node
- LoadBalancer: external access via cloud load balancer
- Headless: direct pod IPs without a virtual IP
- ExternalName: DNS alias to an external service
- When to use which type
Overview
Section titled “Overview”| Type | Accessible From | Use Case |
|---|---|---|
| ClusterIP | Inside cluster only | Service-to-service communication |
| NodePort | Outside via <NodeIP>:<port> | Dev/testing, on-prem without LB |
| LoadBalancer | Outside via cloud LB | Production external access |
| Headless | Inside cluster (pod IPs) | StatefulSets, service discovery |
| ExternalName | Inside cluster (DNS alias) | Aliasing external services |
Deploy
Section titled “Deploy”Navigate to the demo directory:
cd demos/service-typeskubectl apply -f manifests/namespace.yamlkubectl apply -f manifests/app.yamlkubectl apply -f manifests/clusterip.yamlkubectl apply -f manifests/nodeport.yamlkubectl apply -f manifests/loadbalancer.yamlkubectl apply -f manifests/headless.yamlkubectl apply -f manifests/externalname.yamlCheck all services:
kubectl get svc -n service-types-demoTest Each Service Type
Section titled “Test Each Service Type”ClusterIP (internal only)
Section titled “ClusterIP (internal only)”This service is only reachable from inside the cluster:
# This works from inside the clusterkubectl run curl --rm -it --image=curlimages/curl -n service-types-demo -- \ curl -s http://echo-clusterip
# You CANNOT reach it from your machine directly# Use port-forward instead:kubectl port-forward svc/echo-clusterip 8080:80 -n service-types-demo# Then open http://localhost:8080NodePort (exposed on node)
Section titled “NodePort (exposed on node)”Reachable from outside the cluster on port 30080:
# Get the minikube IPminikube ip
# Access directly (no port-forward needed)curl http://$(minikube ip):30080Or use minikube’s helper:
minikube service echo-nodeport -n service-types-demo --urlLoadBalancer (cloud LB)
Section titled “LoadBalancer (cloud LB)”On minikube, LoadBalancer stays in “Pending” unless you run minikube tunnel:
# In a separate terminal, start the tunnelminikube tunnel
# Now check the EXTERNAL-IPkubectl get svc echo-loadbalancer -n service-types-demo
# Access via the external IPcurl http://<EXTERNAL-IP>Headless (direct pod IPs)
Section titled “Headless (direct pod IPs)”A headless service returns pod IPs instead of a virtual IP. DNS resolves to all pod addresses:
# Notice clusterIP is "None"kubectl get svc echo-headless -n service-types-demo
# From inside the cluster, DNS returns individual pod IPskubectl run dig --rm -it --image=busybox:1.36 -n service-types-demo -- \ nslookup echo-headless.service-types-demo.svc.cluster.localCompare with ClusterIP DNS (returns one virtual IP):
kubectl run dig --rm -it --image=busybox:1.36 -n service-types-demo -- \ nslookup echo-clusterip.service-types-demo.svc.cluster.localExternalName (DNS alias)
Section titled “ExternalName (DNS alias)”An ExternalName service creates a CNAME record. No proxying, no pods:
# Notice there's no cluster IP or endpointskubectl get svc external-api -n service-types-demokubectl get endpoints external-api -n service-types-demo
# From inside the cluster, the service name resolves to httpbin.orgkubectl run curl --rm -it --image=curlimages/curl -n service-types-demo -- \ curl -s http://external-api/getWhat is Happening
Section titled “What is Happening”manifests/ namespace.yaml # service-types-demo namespace app.yaml # echoserver Deployment (2 replicas) clusterip.yaml # ClusterIP service (internal only) nodeport.yaml # NodePort on port 30080 loadbalancer.yaml # LoadBalancer (needs minikube tunnel) headless.yaml # Headless (clusterIP: None) externalname.yaml # ExternalName pointing to httpbin.orgHow traffic flows for each type:
ClusterIP: Pod ──> kube-proxy ──> Pod (cluster internal only)NodePort: Client ──> Node:30080 ──> kube-proxy ──> PodLoadBalancer: Client ──> Cloud LB ──> Node:NodePort ──> kube-proxy ──> PodHeadless: Pod ──> DNS lookup ──> Pod IP directly (no kube-proxy)ExternalName: Pod ──> DNS CNAME ──> external.host.comWhen to Use What
Section titled “When to Use What”| Scenario | Service Type |
|---|---|
| Microservice calling another microservice | ClusterIP |
| Expose a service during local development | NodePort |
| Production external traffic (cloud) | LoadBalancer |
| Production external traffic (on-prem) | NodePort + external LB, or Ingress |
| StatefulSet pod discovery | Headless |
| Point to an external database or API | ExternalName |
| HTTP routing by path/host | Ingress (not a Service type, but uses ClusterIP) |
Experiment
Section titled “Experiment”-
Compare endpoints for ClusterIP vs Headless:
Terminal window kubectl get endpoints echo-clusterip -n service-types-demokubectl get endpoints echo-headless -n service-types-demo -
Scale the Deployment and watch endpoints update:
Terminal window kubectl scale deployment echo-server --replicas=4 -n service-types-demokubectl get endpoints echo-headless -n service-types-demo -
Check iptables rules created by kube-proxy:
Terminal window minikube ssh -- sudo iptables -t nat -L KUBE-SERVICES | grep echo
Cleanup
Section titled “Cleanup”kubectl delete namespace service-types-demoFurther Reading
Section titled “Further Reading”See docs/deep-dive.md for a detailed explanation of kube-proxy modes (iptables vs IPVS), EndpointSlices, session affinity, external traffic policy, and Ingress vs Gateway API.
Next Step
Section titled “Next Step”Move on to ConfigMaps & Secrets to learn how to manage application configuration and sensitive data.