AI-assisted note: I wrote the checks from the ones I actually run, and an AI tool helped with the wording. The behaviour is checked against the Kubernetes documentation on Services. I run LearnSome.tech, where we teach Kubernetes; nothing here depends on it.
Most "my Service is broken" problems aren't about the Service type at all. They are a selector that matches nothing, pods that aren't Ready, or a port that points at the wrong place. But it helps to know what each type promises first.
The types in one table
| Type | Who can reach it | Builds on |
|---|---|---|
| ClusterIP (the default) | Clients inside the cluster | Nothing, it's the base |
| NodePort | Anyone who can reach a node, on a port from 30000-32767 by default | ClusterIP |
| LoadBalancer | Clients of the cloud load balancer | NodePort |
| ExternalName | Not a proxy: in-cluster clients get a DNS alias to another name | No cluster IP at all |
The types nest: a NodePort Service also gets a cluster IP, and a LoadBalancer Service still allocates a node port by default. That last one surprises people when they look at firewall rules:
$ kubectl get svc web NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) web LoadBalancer 10.96.12.40 203.0.113.10 80:31234/TCP 80 is the Service port; 31234 is a node port that is open on every node too. Pick the narrowest type that still reaches your clients.
The five checks
When a Service exists but its pods never see a request, I go through these in order and stop at the first one that fails.
# 1. Does the Service exist, with the type and ports I expect? kubectl get svc web -o wide # 2. Did its selector find any pods? An empty endpoint list means no. kubectl get endpointslices -l kubernetes.io/service-name=web # 3. Selector vs pod labels, character by character kubectl get svc web -o jsonpath='{.spec.selector}'; echo kubectl get pods --show-labels # 4. Are the pods Ready? Pods that aren't Ready don't get Service traffic. kubectl get pods -l app=web # 5. Does targetPort match the port the container listens on? kubectl get svc web -o jsonpath='{.spec.ports[*].targetPort}'; echo Step 2 is the one that saves the most time. Kubernetes keeps EndpointSlice objects in step with each Service's selector and with pod readiness, so an empty slice tells you the problem is the selector or readiness, not DNS and not the network.
Step 4 catches a sneaky one: a readiness probe pointed at the wrong port. The pods show Running, nothing restarts, and the Service still has no endpoints, because a failing readiness probe only takes the pod out of rotation.
If all five pass, bypass the Service and call a pod directly. If that fails too, the problem is in the app, not in Kubernetes.
When a Service isn't the answer
If what you need is HTTP routing from outside the cluster (several paths or hostnames behind one entry point), you want an Ingress or a Gateway in front of ClusterIP Services, not a LoadBalancer per app. The Kubernetes project now recommends Gateway over Ingress, whose API is frozen.
Which of the five checks has caught you most often?
