What are the different Service types, and when do you use each?

7 minintermediateservicesclusteripnodeportloadbalancer

Quick Answer

**ClusterIP** (the default) exposes a Service only inside the cluster, on an internal virtual IP. **NodePort** additionally opens a static port on every node's own IP, making the Service reachable from outside the cluster via any node's address. **LoadBalancer** provisions an external cloud load balancer (on supported cloud providers) that routes to the Service, giving it a real external IP. **ExternalName** maps a Service name to an external DNS name, with no proxying at all — a pure DNS-level alias for something outside the cluster.

Detailed Answer

ClusterIP — internal only (the default)

apiVersion: v1
kind: Service
metadata:
  name: backend-api
spec:
  type: ClusterIP     # default; can be omitted
  selector:
    app: backend-api
  ports:
    - port: 80
      targetPort: 8080

Gets a virtual IP reachable only from inside the cluster. This is the right choice for the overwhelming majority of Services — internal microservice-to-microservice communication (a frontend calling a backend API, an API calling a database) almost never needs to be reachable from outside the cluster directly.

NodePort — reachable via any node's IP, on a static port

spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080     # opened on EVERY node's IP, in the 30000-32767 range by default

Every node in the cluster starts listening on nodePort and forwards traffic to the Service, regardless of whether that specific node is actually running any of the backing Pods. This makes the Service reachable via <any-node-ip>:30080 from outside the cluster — but it's a fairly low-level mechanism (you're responsible for load-balancing across nodes yourself, and the fixed port range is limited) rarely used directly in production; it's more commonly a building block that LoadBalancer Services are implemented on top of.

LoadBalancer — provisions a real external cloud load balancer

spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080

On a supported cloud provider (AWS, GCP, Azure), creating a LoadBalancer Service triggers the cloud provider's integration to provision an actual external load balancer (an AWS ELB/NLB, a GCP Load Balancer) that gets a real, internet-routable IP address and forwards traffic into the cluster (typically via NodePort under the hood). This is the standard way to expose a single Service directly to the internet — but provisioning one external load balancer per Service gets expensive and unwieldy at any real scale, which is exactly the problem Ingress (see that question) solves.

ExternalName — a pure DNS alias, no proxying

spec:
  type: ExternalName
  externalName: my-database.us-east-1.rds.amazonaws.com

Creates no virtual IP and does no traffic proxying at all — it's purely a DNS-level CNAME-like redirect. Any Pod resolving my-service.default.svc.cluster.local gets redirected, at the DNS layer, straight to my-database.us-east-1.rds.amazonaws.com. Useful for giving an external dependency (a managed cloud database, a third-party API) a consistent in-cluster name, so application configuration can refer to a stable internal name even if the actual external address changes later.

Choosing between them

NeedService type
Internal service-to-service communication onlyClusterIP
Low-level external access via node IPs (rare in practice)NodePort
A single Service exposed directly to the internetLoadBalancer
Many services need to be exposed under one external IP/domain, with path/host-based routingIngress (fronting ClusterIP Services)
A stable internal name for an external dependencyExternalName

In practice, most production clusters use ClusterIP for internal services and a small number of LoadBalancer Services (or, more commonly, a single one fronting an Ingress controller) rather than exposing many individual Services externally.

Related Resources