Kubernetes Service Types

A detailed comparison of the four Kubernetes Service types — when to use each, their YAML patterns, and their behavior in different environments. Synthesized from CKA Day 9 — Kubernetes Services Explained.

Quick Comparison

TypeScopeExternal AccessUse CaseCKA Relevance
ClusterIPInternal only❌ NoPod-to-Pod communicationVery High
NodePortExternal via Node IP✅ Port 30,000–32,767Dev, bare metal, no cloud LBHigh
LoadBalancerExternal via cloud LB✅ Auto-provisioned IPProduction on cloud providersMedium
ExternalNameDNS alias❌ (redirects)External database/API integrationLow

1. ClusterIP (Default)

ClusterIP is the default Service type. It assigns an internal IP address that is reachable only from within the cluster.

When to Use

  • Front-end Pods communicating with back-end Pods
  • Microservices talking to each other internally
  • Any traffic that should never leave the cluster

YAML

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  type: ClusterIP          # Optional — defaults to ClusterIP if omitted
  selector:
    app: backend
  ports:
    - port: 8080
      targetPort: 8080

Access Patterns

# From another Pod in the same namespace
curl http://backend-svc:8080
 
# Fully Qualified Domain Name (FQDN)
curl http://backend-svc.default.svc.cluster.local:8080

Key Characteristics

  • Most secure — no external exposure
  • Zero cloud-provider dependency
  • If type is omitted, Kubernetes assumes ClusterIP
  • The kubernetes default Service in default namespace is a ClusterIP pointing to the API Server

2. NodePort

NodePort extends ClusterIP by exposing the Service on a static port (30,000–32,767) on every node’s IP address.

When to Use

  • Local development and testing
  • Bare-metal clusters without a cloud load balancer
  • Quick external access when you don’t need a fancy LB

YAML

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30001      # Must be in range 30000-32767

Access Patterns

# From outside the cluster (any node IP)
curl http://<NodeIP>:30001
 
# Check which node a Pod runs on
kubectl get pods -o wide
kubectl describe pod <pod-name> | grep Node:

Internal Behavior

Even with NodePort, the Service still creates a ClusterIP internally. The flow is:

External Client
      │
      ▼
NodeIP:30001  ──▶  kube-proxy  ──▶  ClusterIP  ──▶  Pod:targetPort

Kind Cluster Gotcha

Kind runs Kubernetes inside Docker containers. By default, NodePorts are not exposed to the host. You must explicitly map the port:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30001
        hostPort: 30001

Then recreate the cluster. On cloud VMs, bare metal, or the CKA exam, this extra step is unnecessary. Source: CKA Day 9

3. LoadBalancer

LoadBalancer extends NodePort by provisioning an external cloud load balancer. It provides a single public IP or DNS name that distributes traffic across all nodes.

When to Use

  • Production workloads on AWS, Azure, GCP, or any cloud with LB integration
  • When you need a stable public endpoint (myapp.com)
  • When you want the cloud provider to handle health checks and traffic distribution

YAML

apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80

Behavior Without Cloud Provider

On clusters without cloud-provider integration (e.g., local Kind, on-prem without CCM):

kubectl get svc nginx-lb
# NAME       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)
# nginx-lb   LoadBalancer   10.96.123.45    <pending>     80:31234/TCP
  • EXTERNAL-IP stays <pending> indefinitely
  • Kubernetes still allocates a random nodePort (e.g., 31234)
  • The Service behaves exactly like a NodePort — accessible via NodeIP:31234
  • In Kind, you can simulate a real LoadBalancer with cloud-provider-kind

Cloud Provider Integration

CloudIntegration
AWSCreates a Classic ELB or NLB
AzureCreates an Azure Load Balancer
GCPCreates a GCP Network LB
On-PremRequires MetalLB or cloud-provider-kind

4. ExternalName

ExternalName is fundamentally different — it does not route to Pods at all. Instead, it acts as a DNS CNAME alias to an external domain.

When to Use

  • Your database lives outside the cluster (RDS, Cloud SQL, managed DB)
  • You want application code to reference a Kubernetes Service name (db-svc) while the actual resolution points to prod-db.example.com
  • Migrating external services into Kubernetes gradually

YAML

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: database.example.com

Access Patterns

# From inside the cluster, this resolves to database.example.com
curl http://external-db
 
# DNS lookup
nslookup external-db.default.svc.cluster.local
# Server:  database.example.com

Key Characteristics

  • No selector — it doesn’t select Pods
  • No ports section needed
  • No ClusterIP assigned
  • Pure DNS redirection; the actual connection is made to the external domain
  • If the external domain changes, update the Service — application code stays unchanged

Selector Behavior Across Types

Service TypeRequires Selector?Selects Pods?Creates Endpoints?
ClusterIPYesYesYes
NodePortYesYesYes
LoadBalancerYesYesYes
ExternalNameNoNoNo

Choosing the Right Service Type

Is the traffic internal only?
  ├── YES → ClusterIP
  └── NO → Is this a cloud production environment?
            ├── YES → LoadBalancer
            └── NO → Do you need external access?
                        ├── YES → NodePort
                        └── NO → ExternalName (external DNS alias)

Exam Traps & Common Mistakes

  1. Case Sensitivity: NodePort and LoadBalancer must use capital N/P and L/B. nodeport or loadbalancer will be rejected.
  2. Selector vs matchLabels: Services use selector: {app: nginx}, NOT selector: matchLabels: {app: nginx}. The latter is for Deployments/ReplicaSets.
  3. NodePort Range: Manually specified nodePort must be 30,000–32,767. Values outside this range are rejected.
  4. Kind Port Mapping: Forgetting extraPortMappings in Kind and wondering why NodePort doesn’t work from localhost.
  5. LoadBalancer on Local: Expecting an external IP on Kind/minikube without a cloud provider or MetalLB.

Sources


Tags: kubernetes services clusterip nodeport loadbalancer externalname networking cka devops yaml