What are the different Service types, and when do you use each?
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
| Need | Service type |
|---|---|
| Internal service-to-service communication only | ClusterIP |
| Low-level external access via node IPs (rare in practice) | NodePort |
| A single Service exposed directly to the internet | LoadBalancer |
| Many services need to be exposed under one external IP/domain, with path/host-based routing | Ingress (fronting ClusterIP Services) |
| A stable internal name for an external dependency | ExternalName |
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.