In this guide, you’ll learn about the four primary Kubernetes Service types—ClusterIP, NodePort, Headless, and ExternalName—using a sample NGINX deployment. Understanding these service types will help you expose applications both inside and outside your cluster.
Overview of Service Types
Service Type Scope Use Case Example Port ClusterIP Internal cluster traffic Internal load-balancing 80NodePort Node’s IP + port External access via node IP and port 30000Headless Pod IPs directly Direct per-pod connectivity (no LB) 80ExternalName DNS CNAME mapping Map service to external DNS (no proxy) N/A
Before diving in, we’ve deployed three NGINX pods with the label role=nginx in the default namespace:
kubectl get pods
# OUTPUT
# NAME READY STATUS RESTARTS AGE
# nginx-deployment-7ff69d756-8qdv8 1/1 Running 0 3m
# nginx-deployment-7ff69d756-hccjn 1/1 Running 0 3m
# nginx-deployment-7ff69d756-stpmz 1/1 Running 0 3m
1. ClusterIP (Default)
ClusterIP is the Kubernetes default service type. It allocates a virtual IP reachable only within the cluster.
1.1 Service Definition
apiVersion : v1
kind : Service
metadata :
name : clusterip-svc
namespace : default
spec :
type : ClusterIP
selector :
role : nginx
ports :
- name : http
port : 80
targetPort : 80
kubectl apply -f clusterip-svc.yaml
kubectl get svc clusterip-svc
# OUTPUT
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# clusterip-svc ClusterIP 10.102.157.139 <none> 80/TCP 5m
1.2 Testing Internal Access
Launch a temporary pod to test DNS and HTTP:
kubectl run -i --tty --rm debug --image=curlimages/curl --restart=Never -- sh
Inside the debug pod:
nslookup clusterip-svc.default.svc.cluster.local
curl http://clusterip-svc.default.svc.cluster.local
# Should return the NGINX welcome page
ClusterIP services are only reachable from within the Kubernetes cluster. Use them for internal microservice communication.
2. NodePort
NodePort exposes a Service on each Node’s IP at a static port, allowing external traffic.
2.1 Service Definition
apiVersion : v1
kind : Service
metadata :
name : nodeport-svc
namespace : default
spec :
type : NodePort
selector :
role : nginx
ports :
- name : http
port : 80
targetPort : 80
nodePort : 30000
kubectl apply -f nodeport-svc.yaml
kubectl get svc nodeport-svc
# OUTPUT
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# nodeport-svc NodePort 10.98.229.84 <none> 80:30000/TCP 5m
2.2 Access via Node IP
Find a node’s IP address:
kubectl get node node01 -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}'
# e.g., 192.168.121.156
From outside the cluster:
curl http://192.168.121.156:30000
This should return the NGINX welcome page.
Ensure that your cloud provider’s firewall or on-premise network allows traffic to the nodePort range (default 30000–32767).
2.3 Internal DNS Resolution
Within the cluster, you can still resolve the service by DNS:
kubectl run -i --tty --rm debug --image=curlimages/curl --restart=Never -- sh
# Inside the pod:
nslookup nodeport-svc.default.svc.cluster.local
curl http://nodeport-svc.default.svc.cluster.local
3. Headless Service
A headless Service omits the cluster IP (clusterIP: None) and returns the IPs of individual pods directly.
3.1 Service Definition
apiVersion : v1
kind : Service
metadata :
name : headless-svc
namespace : default
spec :
clusterIP : None
selector :
role : nginx
ports :
- name : http
port : 80
targetPort : 80
kubectl apply -f headless-svc.yaml
kubectl get svc headless-svc
# OUTPUT
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# headless-svc ClusterIP None <none> 80/TCP 5m
3.2 DNS and Direct Pod Access
kubectl run -i --tty --rm debug --image=curlimages/curl --restart=Never -- sh
Inside the debug pod:
nslookup headless-svc.default.svc.cluster.local
for ip in $( nslookup headless-svc.default.svc.cluster.local | grep Address | awk '{print $2}' ); do
curl http:// $ip
done
Headless Services are ideal for stateful applications (e.g., databases) where you need direct pod access for persistent storage or custom load balancing.
4. ExternalName
ExternalName maps a Service to an external DNS name by returning a CNAME record.
4.1 Service Definition
apiVersion : v1
kind : Service
metadata :
name : externalname-svc
namespace : default
spec :
type : ExternalName
externalName : httpbin.org
kubectl apply -f externalname-svc.yaml
kubectl get svc externalname-svc
# OUTPUT
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# externalname-svc ExternalName <none> <none> <none> 5m
4.2 Testing ExternalName
kubectl run -i --tty --rm debug --image=curlimages/curl --restart=Never -- sh
# Inside the pod:
curl http://externalname-svc.default.svc.cluster.local/get
# This request is forwarded to httpbin.org/get
ExternalName does not proxy traffic through the cluster—it simply performs a DNS CNAME lookup. Use this to reference external APIs or services.
Further Reading & References