ClusterIP

ClusterIP is the default and most common Service type in Kubernetes. It provides a stable, cluster-internal IP address that allows pods and other cluster resources to communicate with a set of pods. Think of ClusterIP as an internal phone number—it works perfectly within your organization (cluster), but external callers can’t reach it directly.

What is ClusterIP?

A ClusterIP Service creates a virtual IP address that’s only accessible from within the Kubernetes cluster. This IP address is stable—it doesn’t change even if the pods behind it are recreated, rescheduled, or scaled. The Service automatically load balances traffic across all pods matching its selector.

graph TB subgraph cluster[Kubernetes Cluster] A[Pod A] --> B[ClusterIP Service<br/>10.96.0.1] C[Pod B] --> B D[Pod C] --> B B --> E[Backend Pod 1<br/>10.244.1.5] B --> F[Backend Pod 2<br/>10.244.1.6] B --> G[Backend Pod 3<br/>10.244.2.3] end H[External Client] -.->|Cannot Access| B style B fill:#e8f5e9 style H fill:#ffe1e1 style E fill:#fff4e1 style F fill:#fff4e1 style G fill:#fff4e1

How ClusterIP Works

When you create a ClusterIP Service, Kubernetes:

  1. Allocates a virtual IP from the cluster’s service CIDR (typically 10.96.0.0/12)
  2. Creates DNS records for service discovery
  3. Watches for matching pods using the Service selector
  4. Updates Endpoints as pods are added or removed
  5. Routes traffic using kube-proxy on each node
graph LR A[Service Created] --> B[IP Allocated: 10.96.0.1] B --> C[DNS Record Created] C --> D[Selector Evaluates Pods] D --> E[Endpoints Updated] E --> F[kube-proxy Configures Routing] F --> G[Service Ready] style A fill:#e1f5ff style G fill:#e8f5e9

ClusterIP Service Example

Here’s a basic ClusterIP Service:

apiVersion: v1
kind: Service
metadata:
  name: web-service
  namespace: default
spec:
  type: ClusterIP  # This is the default, can be omitted
  selector:
    app: web
    tier: frontend
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

Key points:

  • type: ClusterIP is the default—you can omit it
  • selector must match labels on your pods
  • port is the Service port (what clients connect to)
  • targetPort is the pod container port (where traffic is forwarded)

Service IP Allocation

ClusterIP addresses are allocated from the cluster’s service CIDR range, which is configured when the cluster is set up. The IPs are managed by Kubernetes and remain stable for the lifetime of the Service.

graph TB A[Service CIDR<br/>10.96.0.0/12] --> B[Service 1: 10.96.0.1] A --> C[Service 2: 10.96.0.2] A --> D[Service 3: 10.96.0.3] A --> E[...] style A fill:#e1f5ff style B fill:#e8f5e9 style C fill:#e8f5e9 style D fill:#e8f5e9

DNS Discovery

ClusterIP Services are automatically discoverable via DNS. Within the same namespace, you can use the short name:

# Pod connecting to Service
apiVersion: v1
kind: Pod
metadata:
  name: client-pod
spec:
  containers:
  - name: app
    image: busybox
    command: ['sh', '-c', 'wget -O- http://web-service:80']

DNS resolution:

  • Short name: web-service (same namespace)
  • FQDN: web-service.default.svc.cluster.local (any namespace)
  • Cross-namespace: web-service.production.svc.cluster.local
graph LR A[Pod] --> B[DNS Query: web-service] B --> C[CoreDNS] C --> D[Returns: 10.96.0.1] D --> E[Pod Connects to Service IP] E --> F[Service Routes to Backend Pods] style A fill:#e1f5ff style C fill:#fff4e1 style D fill:#e8f5e9 style F fill:#fff4e1

Load Balancing

ClusterIP Services automatically distribute traffic across all healthy pods matching the selector. The default load balancing algorithm is round-robin, but the exact behavior depends on the kube-proxy mode (iptables, ipvs, or userspace).

graph TB A[Client Request 1] --> B[ClusterIP Service] B --> C[Pod 1] D[Client Request 2] --> B B --> E[Pod 2] F[Client Request 3] --> B B --> G[Pod 3] H[Client Request 4] --> B B --> C style B fill:#e8f5e9 style C fill:#fff4e1 style E fill:#fff4e1 style G fill:#fff4e1

Complete Example: Frontend to Backend

Here’s a complete example showing a frontend Service connecting to a backend Service:

Backend Deployment and Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: api
        image: my-api:1.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
  - port: 80
    targetPort: 8080

Frontend Deployment using Backend Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: web
        image: my-frontend:1.0
        env:
        - name: BACKEND_URL
          value: "http://backend-service:80"  # Uses ClusterIP Service
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 3000
graph TB subgraph frontend[Frontend] A[Frontend Pod 1] --> B[Frontend Service<br/>ClusterIP] C[Frontend Pod 2] --> B end subgraph backend[Backend] B --> D[Backend Service<br/>ClusterIP] D --> E[Backend Pod 1] D --> F[Backend Pod 2] D --> G[Backend Pod 3] end style B fill:#e8f5e9 style D fill:#e8f5e9 style E fill:#fff4e1 style F fill:#fff4e1 style G fill:#fff4e1

Multiple Ports

ClusterIP Services can expose multiple ports:

apiVersion: v1
kind: Service
metadata:
  name: multi-port-service
spec:
  selector:
    app: my-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
  - name: https
    port: 443
    targetPort: 8443
    protocol: TCP
  - name: metrics
    port: 9090
    targetPort: 9090
    protocol: TCP

Clients can connect to specific ports:

  • http://multi-port-service:80 → pod port 8080
  • https://multi-port-service:443 → pod port 8443
  • http://multi-port-service:9090 → pod port 9090

Session Affinity

By default, ClusterIP Services distribute requests across pods. You can enable session affinity to route requests from the same client to the same pod:

apiVersion: v1
kind: Service
metadata:
  name: sticky-service
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800  # 3 hours

Use cases:

  • Applications that store session data in memory
  • Sticky sessions for stateful workloads
  • Applications that benefit from connection reuse

When to Use ClusterIP

Use ClusterIP when:

Internal communication - Services that only need to be accessed from within the cluster
Microservices architecture - Service-to-service communication
Database connections - Internal database access (don’t expose databases externally)
API backends - Backend APIs consumed by frontend services
Default choice - When you don’t need external access, ClusterIP is the right choice

Don’t use ClusterIP when:

  • You need external internet access
  • You’re running on bare metal without a load balancer
  • You need direct external client access

ClusterIP vs Other Service Types

graph TB subgraph clusterip[ClusterIP] A[Internal Only] B[Stable IP] C[DNS Discovery] end subgraph nodeport[NodePort] D[External Access] E[Node IP:Port] F[Development] end subgraph loadbalancer[LoadBalancer] G[External IP] H[Cloud Provider] I[Production] end style clusterip fill:#e8f5e9 style nodeport fill:#fff4e1 style loadbalancer fill:#f3e5f5

Best Practices

  1. Use ClusterIP by default - It’s the most secure and appropriate for internal services
  2. Don’t expose databases externally - Use ClusterIP for databases, access via bastion or VPN
  3. Use descriptive Service names - Names should indicate the service purpose
  4. Match selectors exactly - Ensure Service selector matches pod labels precisely
  5. Document port mappings - Clearly document which Service ports map to which pod ports
  6. Use port names - Name ports for clarity, especially with multiple ports
  7. Test DNS resolution - Verify Services are discoverable via DNS
  8. Monitor Endpoints - Ensure Services are finding and routing to the correct pods

Troubleshooting

Service Not Accessible

  1. Check Service exists: kubectl get service <service-name>
  2. Verify Endpoints: kubectl get endpoints <service-name> - should list pod IPs
  3. Check selector matches pods: kubectl get pods -l app=my-app
  4. Test DNS resolution: kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup <service-name>

DNS Not Resolving

  1. Use FQDN: Try <service>.<namespace>.svc.cluster.local instead of short name
  2. Check namespace: Ensure client and Service are in the same namespace (or use FQDN)
  3. Verify CoreDNS: kubectl get pods -n kube-system -l k8s-app=kube-dns

Traffic Not Reaching Pods

  1. Check targetPort: Verify targetPort matches the container’s listening port
  2. Test pod directly: kubectl port-forward <pod-name> <port> to test pod connectivity
  3. Check pod logs: Verify pods are running and listening on the expected port
  4. Verify Endpoints: kubectl describe endpoints <service-name> to see which pods are included

Connection Refused

  1. Pod not listening: Check if the container is actually listening on targetPort
  2. Wrong port: Verify the targetPort in Service matches container port
  3. Pod not ready: Check pod readiness probes and status

See Also