Kubernetes Services: ClusterIP, NodePort, LoadBalancer, Ingress

Master Kubernetes service types and Ingress controllers to expose your applications inside and outside the cluster with proper load balancing and routing.

published: reading time: 19 min read author: GeekWorkBench

Kubernetes Services: ClusterIP, NodePort, LoadBalancer, and Ingress

Pods in Kubernetes are ephemeral. They get IP addresses assigned when they start and those addresses change when pods reschedule. If you want your application to be reachable, you need something stable. Kubernetes Services provide that stability by creating a persistent endpoint for a set of pods.

This post covers the four service types, when to use each one, and how Ingress controllers extend routing beyond simple port forwarding.

If you need to understand the basics of Kubernetes first, check the Kubernetes fundamentals post. For advanced networking patterns, see the Advanced Kubernetes post.

Introduction

Pod IPs change when pods reschedule. If your app depends on a fixed address, it breaks constantly. Kubernetes Services give you a stable virtual IP that load-balances across matching pods, which is what makes microservices actually work in practice.

External traffic is a separate problem. Kubernetes gives you four service types and Ingress resources for different exposure patterns. Picking the wrong one costs money (over-provisioned load balancers) or creates weird URLs that users cannot reach. Getting it right means understanding how traffic actually flows from the outside world to your container.

This post covers the four service types, when to use each, and how Ingress controllers add HTTP routing on top. You will see how a request travels from a user to your pod, the trade-offs between each approach, and how to fix the most common networking problems you will hit in production.

Service Types Comparison

Kubernetes offers four service types:

Decision Matrix: Choosing the Right Service Type

Access ScenarioService TypeExample
Internal microservice-to-microserviceClusterIPAPI to database
Expose single node for debuggingNodePortDev environment access
Production HTTP/HTTPS trafficIngressWeb app frontend
TCP/UDP without Ingress complexityLoadBalancerCustom protocol, legacy apps
Cross-cluster service discoveryExternalNameIntegrate external service

Skip NodePort for production HTTP services. The port range (30000-32767) is awkward, and node IPs change in dynamic clusters.

Skip LoadBalancer per microservice. One load balancer per team or product boundary is usually enough. Provisioning a cloud LB for every pod gets expensive fast.

ClusterIP is internal only. If you need external access, ClusterIP is not your answer.

Traffic Flow Architecture

Here is how a request moves from an external user down to a pod:

flowchart TD
    User([External User]) --> Internet[Internet]
    Internet --> DNS[DNS Resolution<br/>api.example.com]
    DNS --> ALB[Cloud Load Balancer<br/>NLB/ALB]
    ALB --> Ingress[Ingress Controller<br/>NGINX/Traefik]
    Ingress --> SvcClusterIP[ClusterIP Service<br/>kube-proxy routes to Pods]
    Ingress --> SvcNodePort[NodePort Service<br/>:30000-32767 on each Node]
    Ingress --> SvcLB[LoadBalancer Service<br/>Cloud-managed LB]
    SvcClusterIP --> Pod1[Pod<br/>app=v1]
    SvcClusterIP --> Pod2[Pod<br/>app=v1]
    SvcClusterIP --> Pod3[Pod<br/>app=v1]
    SvcNodePort --> Node1[Node<br/>kube-proxy]
    Node1 --> Pod1
    SvcLB --> Pod1
    SvcLB --> Pod2
    SvcLB --> Pod3

For most production HTTP/HTTPS workloads, the typical path is: User → DNS → Load Balancer → Ingress → ClusterIP Service → Pods.

For TCP services without Ingress, the path skips the Ingress step and goes directly: User → DNS → Load Balancer Service → Pods.

NodePort is mainly for development. The path there is: User → Node IP and port → Pod.


Service Types Overview

TypeAccess ScopeUse Case
ClusterIPInternal onlyMicroservices within the cluster
NodePortExposes on each node IPDevelopment, simple external access
LoadBalancerExternal via cloud LBProduction external access
ExternalNameMaps to external DNSIntegrating external services

ClusterIP is the default. You get an internal cluster IP that pods can use to communicate with each other. The other types expose services outside the cluster.

ClusterIP for Internal Access

ClusterIP is the most common service type. It creates an internal IP that load-balances traffic across all matching pods. Other pods in the cluster use the service name to reach your application.

apiVersion: v1
kind: Service
metadata:
  name: api-backend
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: api-backend
    version: v2
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8080
    - name: grpc
      protocol: TCP
      port: 50051
      targetPort: 50051

The targetPort can be a number or a name that matches the container port. Using names makes it easier to update ports without changing the service.

Within the cluster, pods access the service using its fully qualified name:

http://api-backend.production.svc.cluster.local

Or just the service name if they are in the same namespace:

http://api-backend

DNS is automatic. Kubernetes maintains a DNS entry for every service.

Headless Services for StatefulSets

Set clusterIP: None to create a headless service. Instead of load balancing, DNS returns the pod IPs directly. This is useful for StatefulSets where clients need to discover individual pod addresses.

apiVersion: v1
kind: Service
metadata:
  name: postgres-cluster
  namespace: database
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - port: 5432

With a headless service, DNS queries return A records for each pod: postgres-cluster-0.postgres-cluster.database.svc.cluster.local, and so on.

NodePort for Development

NodePort opens a port on every node in the cluster. Traffic arriving at http://<node-ip>:<node-port> gets routed to the service. The port range defaults to 30000-32767.

apiVersion: v1
kind: Service
metadata:
  name: web-frontend-nodeport
spec:
  type: NodePort
  selector:
    app: web-frontend
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080

Setting nodePort is optional. Kubernetes assigns one from the default range if you omit it.

NodePort works for development and quick demos. For production, use LoadBalancer or Ingress. NodePort bypasses some load balancing logic and exposes infrastructure details you may not want.

LoadBalancer with Cloud Controllers

On cloud providers that support external load balancers (AWS, GCP, Azure), the LoadBalancer type provisions a managed load balancer and routes traffic to your service.

apiVersion: v1
kind: Service
metadata:
  name: web-frontend-lb
  namespace: production
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  type: LoadBalancer
  selector:
    app: web-frontend
  ports:
    - port: 80
      targetPort: 80
    - port: 443
      targetPort: 443

The annotation service.beta.kubernetes.io/aws-load-balancer-type: "nlb" creates a Network Load Balancer on AWS instead of a Classic Load Balancer.

Cloud controllers create load balancers with health checks pointed at your pods. They also handle SSL termination if you configure certificates.

For SSL termination on the load balancer, you need to annotate the service with the certificate ARN:

annotations:
  service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-east-1:123456789:certificate/abc123"

Ingress Controllers and Rules

Ingress is not a service type. It is a Kubernetes resource that provides HTTP/HTTPS routing rules. An Ingress controller implements the routing. Without a controller, Ingress resources do nothing.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /users
            pathType: Prefix
            backend:
              service:
                name: users-api
                port:
                  number: 80
          - path: /products
            pathType: Prefix
            backend:
              service:
                name: products-api
                port:
                  number: 80
    - host: admin.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-console
                port:
                  number: 80
  tls:
    - hosts:
        - api.example.com
        - admin.example.com
      secretName: example-com-tls

The ingressClassName field specifies which Ingress controller handles this Ingress. Common controllers include NGINX Ingress Controller, Traefik, and cloud-provider ingress controllers.

Path rewriting

The NGINX ingress controller supports path rewriting via annotations:

annotations:
  nginx.ingress.kubernetes.io/rewrite-target: /$2

With this configuration, requests to /api/v1/users get rewritten to /users before reaching the backend service.

Rate limiting via Ingress

annotations:
  nginx.ingress.kubernetes.io/limit-rps: "10"
  nginx.ingress.kubernetes.io/limit-connections: "5"

These annotations apply rate limiting at the Ingress level, protecting all backend services from excessive traffic.

Security Considerations

Services enable connectivity, but you should restrict which pods can communicate with which. Kubernetes Network Policies (covered in a separate post) let you enforce microsegmentation between pods.

For external access, prefer Ingress with TLS termination over NodePort. Ingress provides path-based routing and centralized SSL management.

Avoid exposing services directly with LoadBalancer unless you need layer 3/4 load balancing. Most web traffic works fine with Ingress and a single load balancer.

Trade-off Analysis

ClusterIP vs Headless Services

AspectClusterIPHeadless (ClusterIP: None)
Load balancingBuilt-in, round-robinNone — client manages pod discovery
DNS resolutionSingle service IPMultiple A records (one per pod)
Use caseStateless microservicesStatefulSets, leader election
Client complexityLowHigh

Service Type Selection Trade-offs

Service TypeProsCons
ClusterIPSimple, internal onlyNo external access
NodePortWorks without cloud providerHigh ports, security concerns, no LB
LoadBalancerFull cloud integrationCost per service, overkill for HTTP
IngressHost/path routing, SSL terminationExtra controller deployment

Ingress vs LoadBalancer per Service

CriteriaIngress ControllerOne LoadBalancer per Service
CostOne LB shared across all servicesOne LB per service
SSL managementCentralizedPer-service or per-LB
HTTP/HTTPS routingPath-based, host-basedPort-based only
Operational overheadController deploymentCloud quota management

For most production HTTP/HTTPS workloads, Ingress with a single LoadBalancer is the most cost-effective approach.

Production Failure Scenarios

ClusterIP Service Not Reachable After Pod Restart

When a pod restarts, its IP changes. If your application hardcodes pod IPs instead of using the ClusterIP service name, communication breaks.

Symptoms: Pod-to-pod communication fails after restarts, Connection refused errors.

Diagnosis:

kubectl get pod -o wide  # Check pod IPs
kubectl get endpoints <service-name>  # Should list pod IPs
kubectl describe pod <pod-name>  # Check status

Mitigation: Always use the ClusterIP service DNS name for pod-to-pod communication, never pod IPs.

NodePort Service Port Conflicts

When multiple services use the same nodePort value, one service fails to start.

Symptoms: nodePort port is already allocated error when applying a Service.

Mitigation: Let Kubernetes assign the port automatically, or track port allocations explicitly. Do not hardcode nodePort values across multiple services without coordination.

LoadBalancer Service Stays Pending

On cloud providers, LoadBalancer provisioning can fail due to quota limits, missing IAM permissions, or unsupported service configurations.

Symptoms: ExternalLB Pending status persists for minutes.

Diagnosis:

kubectl describe service <name> -n <namespace>
# Check events for error messages
kubectl get events --sort-by='.lastTimestamp' -n <namespace>

Mitigation: Verify cloud IAM permissions for the service account. Check cloud quota for load balancers. Use annotations to specify the correct load balancer type (NLB vs CLB on AWS).

Common Pitfalls / Anti-Patterns

Exposing Services Directly Without Ingress

Exposing every microservice with its own LoadBalancer quickly exhausts cloud quotas and gets expensive. Use Ingress for HTTP/HTTPS services and reserve LoadBalancer for TCP-level services or non-HTTP protocols.

Using NodePort in Production

NodePort exposes a service on a high port across all nodes. This is useful for debugging but should not be used for production access. The port range (30000-32767) is not standard, and node IPs change in dynamic clusters.

Skipping Health Checks on Headless Services

For StatefulSets with headless services, clients need working health checks to discover which pod is the primary. Without proper readiness probes, clients may attempt to write to a replica that is not ready.

Interview Questions

1. What is the difference between ClusterIP, NodePort, and LoadBalancer service types in Kubernetes?

Expected answer points:

  • ClusterIP provides internal-only access within the cluster — only other pods can reach it
  • NodePort exposes the service on a high port (30000-32767) on every node's IP — useful for dev but not production HTTP
  • LoadBalancer provisions an external cloud load balancer (AWS ELB, GCP LB, Azure LB) for production external access
  • Ingress is not a service type but a routing layer for HTTP/HTTPS with host and path-based rules
2. How does kube-proxy route traffic to pods behind a ClusterIP service?

Expected answer points:

  • kube-proxy watches for Service and Endpoint changes on each node
  • It programs iptables or IPVS rules to redirect traffic to the service IP:port to backend pod IPs
  • Load balancing is performed at the kernel level using randomized selection among backend pods
  • When endpoints change (pod restarts), iptables/IPVS rules are updated
3. What is a headless service and when would you use it?

Expected answer points:

  • A headless service is created by setting `clusterIP: None`
  • No ClusterIP is assigned — DNS returns individual pod A records instead
  • Clients can discover pod IPs directly for StatefulSet workloads where each pod needs a stable identity
  • Useful for leader election, master-slave database setups, and scenarios requiring direct pod-to-pod communication
4. What is the purpose of the `targetPort` field in a Service definition?

Expected answer points:

  • targetPort specifies the port on the pod that receives traffic
  • It can be a number or a named port matching the container's port definition
  • Using named ports makes updates easier — changing the container port does not require changing the service
  • If omitted, targetPort defaults to the `port` value
5. Why should you avoid using NodePort for production HTTP services?

Expected answer points:

  • NodePort uses a non-standard port range (30000-32767) that is not typical for end users
  • Node IPs are dynamic in managed clusters — using them requires additional discovery or stable host entries
  • No built-in SSL termination, path-based routing, or host-based routing
  • Traffic bypasses load balancing logic that Ingress or LoadBalancer types provide
6. How does an Ingress controller differ from a LoadBalancer service?

Expected answer points:

  • Ingress is a Kubernetes resource; an Ingress controller is the implementation that acts on it
  • Ingress provides HTTP/HTTPS routing with host-based and path-based rules
  • LoadBalancer is layer 4 (TCP/UDP) — one per service, no path routing
  • A single Ingress controller can route many services through one shared LoadBalancer, reducing cost
7. What annotations are commonly used with AWS LoadBalancer services?

Expected answer points:

  • `service.beta.kubernetes.io/aws-load-balancer-type: "nlp"` — specifies NLB vs Classic LB
  • `service.beta.kubernetes.io/aws-load-balancer-ssl-cert: ` — assigns ACM certificate for SSL termination
  • `service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp"` — for HTTP vs TCP backends
  • `service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"` — distributes traffic across AZs
8. What happens to client connections when a backend pod behind a ClusterIP service terminates?

Expected answer points:

  • kube-proxy updates iptables rules to remove the terminated pod from the endpoint list
  • In-flight requests to the terminated pod fail with connection reset
  • New requests route to remaining healthy pods
  • Clients should implement retry logic with backoff to handle transient failures during pod churn
9. How does `sessionAffinity` work in a Kubernetes Service?

Expected answer points:

  • Default is `None` — iptables routes each connection to a random backend pod
  • `ClientIP` affinity hashes the source IP and routes to the same pod for a configurable timeout
  • Session affinity is useful for stateful protocols where the client must reach the same backend
  • It is not a substitute for sticky sessions at the application level — it only guarantees the same backend pod
10. What are the limitations of Kubernetes Services for east-west traffic management?

Expected answer points:

  • Services do not provide fine-grained traffic control (canary releases, traffic splitting)
  • No built-in circuit breaking, retry policies, or rate limiting at the service mesh level
  • NetworkPolicies provide L3/L4 isolation but not L7 traffic management
  • Service mesh solutions (Istio, Linkerd) extend Services with L7 control, mTLS, and observability
11. How do you troubleshoot a ClusterIP service that is not routing traffic to pods?

Expected answer points:

  • Verify endpoints exist: `kubectl get endpoints ` should list pod IPs
  • Check pod selector labels match: `kubectl describe service ` shows selector
  • Verify pods are running and ready: `kubectl get pods -l app=`
  • Check kube-proxy logs on nodes for iptables programming issues
  • Test connectivity from another pod using the service DNS name
12. What is the difference between `LoadBalancer` and `ClusterIP` service type regarding external traffic?

Expected answer points:

  • ClusterIP is purely internal — no external access by design
  • LoadBalancer triggers cloud provider to provision an external load balancer that routes to the service
  • The cloud LB has an external IP/DNS that external clients use
  • LoadBalancer is more expensive and best reserved for TCP/UDP protocols or when Ingress is not suitable
13. Can a single Ingress resource handle multiple hostnames? How?

Expected answer points:

  • Yes — an Ingress spec can contain multiple `rules` entries, each with a different `host`
  • Each host can have its own set of paths routing to different backend services
  • A single TLS block can cover multiple hosts via multiple entries in the `hosts` list
  • Wildcard hosts (e.g., `*.example.com`) can be used to match all subdomains
14. How does pathType: Prefix differ from pathType: Exact in Ingress?

Expected answer points:

  • Exact match: URL path must match the path string exactly (e.g., `/users` matches only `/users`)
  • Prefix match: URL path must have the prefix at the start (e.g., `/users` matches `/users`, `/users/123`, `/users/foo/bar`)
  • Prefix is more commonly used and is the default if pathType is omitted
  • Exact is useful when you need strict path boundaries and no unintended prefix matching
15. What is the role of the `endpoints` object in a Kubernetes Service?

Expected answer points:

  • Endpoints is an auto-created resource that lists the IP addresses of pods matching the service selector
  • When a pod is added or removed, the Endpoints object is updated automatically
  • kube-proxy watches Endpoints and programs routing rules accordingly
  • You can create Headless Services where the Endpoints directly drive DNS A records
16. What does `externalTrafficPolicy: Local` do on a LoadBalancer service and what are its trade-offs?

Expected answer points:

  • `Local` preserves the client source IP address — without it, the load balancer SNATs traffic, hiding the original client IP
  • With `Local`, traffic is only routed to nodes that have at least one backing pod — other nodes terminate the connection at the LB
  • Trade-off: `Local` can create uneven load distribution when pods are not evenly spread across nodes
  • Use `Local` when the application needs the original client IP for logging, compliance, or geo-based routing
17. How do you configure health checks for a Kubernetes Service?

Expected answer points:

  • Health checks are configured on the pod via `readinessProbe` and `livenessProbe` fields, not on the Service itself
  • readinessProbe determines if a pod can receive traffic — failing readiness removes the pod from the Service endpoint list
  • livenessProbe determines if a pod should be restarted — failing liveness triggers container restart
  • Common probe types: `exec` (run a command), `httpGet` (GET request), `tcpSocket` (open a TCP connection)
18. What are EndpointSlices and how do they improve on the original Endpoints object?

Expected answer points:

  • EndpointSlices are a more scalable replacement for Endpoints, introduced to handle large clusters with many pods
  • Endpoints stores all pod IPs in a single object — EndpointSlices splits them across multiple objects of up to 100 IPs each
  • EndpointSlices include topology information (region, zone, hostname) for topology-aware routing
  • kube-proxy and other controllers consume EndpointSlices the same way they consume Endpoints
19. What is the purpose of `service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"` annotation?

Expected answer points:

  • The annotation specifies that the NLB should route directly to pod IPs (IP target) rather than node IPs (instance target)
  • IP target mode eliminates node-level hop and preserves client source IP (like `externalTrafficPolicy: Local`)
  • With IP target, the NLB sends traffic directly to pods across all availability zones simultaneously
  • This mode requires the AWS VPC CNI plugin and works with pods that have ENIs attached in the VPC subnet
20. How does Kubernetes DNS (CoreDNS) handle service discovery and what record types does it create?

Expected answer points:

  • CoreDNS creates A records for ClusterIP services: `..svc.cluster.local` resolves to the ClusterIP
  • For headless services (ClusterIP: None), CoreDNS creates A records for each pod: `..svc.cluster.local` resolves to all pod IPs
  • SRV records are created for named ports: `...svc.cluster.local`
  • ExternalName services create CNAME records pointing to the external FQDN specified

Further Reading

Conclusion

Kubernetes Services provide stable endpoints for your applications. ClusterIP works for internal microservice communication. NodePort is useful for development and testing. LoadBalancer integrates with cloud providers for production external access. Ingress controllers add HTTP/HTTPS routing with host-based and path-based rules, SSL termination, and rate limiting.

Start with ClusterIP for internal traffic. Add Ingress when you need external HTTP/HTTPS access. Use LoadBalancer only when you need TCP-level load balancing or integration with non-HTTP services.

Understanding these networking primitives helps you design services that are reachable, scalable, and secure. For deeper Kubernetes networking concepts like Network Policies, see the Advanced Kubernetes post.

Category

Related Posts

Kubernetes Network Policies: Securing Pod-to-Pod Communication

Implement microsegmentation in Kubernetes using Network Policies to control traffic flow between pods and enforce zero-trust networking.

#kubernetes #network-policies #security

Cloud Security: IAM, Network Isolation, and Encryption

Implement defense-in-depth security for cloud infrastructure—identity and access management, network isolation, encryption, and security monitoring.

#cloud #security #iam

Container Security: Image Scanning and Vulnerability Management

Implement comprehensive container security: from scanning images for vulnerabilities to runtime security monitoring and secrets protection.

#container-security #docker #kubernetes