Kubernetes Service No Endpoints: Debug Checklist
Fix a Kubernetes Service with no endpoints: check EndpointSlices, selectors, readiness, targetPort, terminating Pods, and selectorless Services in order.
Ben Ennis
Published October 6, 2026
A Service with no endpoints fails quietly. The ClusterIP still exists, DNS still resolves, and the Deployment can report every replica as Running. Clients see refused connections, timeouts, or a 503 from the edge. The Kubernetes Service debugging guide treats an empty ENDPOINTS column as its own branch of the investigation, and it deserves one, because the fix almost never lives in the Service’s networking. It lives in labels, readiness, or ports.
Where Kubernetes records a Service’s endpoints
A control loop evaluates every Service selector and writes the result into one or more EndpointSlice objects. The EndpointSlice documentation explains that the label kubernetes.io/service-name links each slice to its Service. By default a slice holds no more than 100 endpoints, and the --max-endpoints-per-slice controller flag can raise that to 1,000. Dual-stack Services get separate IPv4 and IPv6 slices, so never assume a slice name. Always list slices by label:
kubectl get endpointslices -n shop -l kubernetes.io/service-name=checkout
The older Endpoints API still answers, but it is not the source of truth any more. Kubernetes deprecated v1 Endpoints in v1.33, and the API server now warns anyone who reads or writes it. If a runbook or script still uses kubectl get endpoints, switch it to EndpointSlices before you trust its output during an incident.
Each endpoint in a slice carries three conditions, defined in the EndpointSlice API reference:
| Condition | What it means for a Pod-backed endpoint | How proxies use it |
|---|---|---|
serving |
Mirrors the Pod’s Ready condition |
Target for traffic, including during drain |
terminating |
Set once the Pod has a deletion timestamp | Normally ignored; used only if every endpoint is terminating |
ready |
Shortcut for serving and not terminating | The normal routing signal |
“No endpoints” therefore has two forms. Either the slice is empty, or it lists addresses whose ready condition is false. The default kubectl get table does not show conditions, so read them directly:
kubectl get endpointslices -n shop -l kubernetes.io/service-name=checkout -o json \
| jq -r '.items[].endpoints[]? | [.addresses[0], .targetRef.name,
(.conditions.ready|tostring), (.conditions.serving|tostring),
(.conditions.terminating|tostring)] | @tsv'
An empty result means the selector or the Service type is the problem. Rows that show false mean the Pods are matched but are not eligible for traffic.
Branch 1: the selector matches nothing
This is the most common cause, and the debugging guide calls it out by name: a Service that selects app=hostnames while the Deployment labels its Pods run=hostnames. Compare the two directly instead of reading YAML by eye:
kubectl get svc checkout -n shop -o jsonpath='{.spec.selector}'; echo
kubectl get pods -n shop -l app=checkout,tier=api --show-labels
Every key in the selector must match, so one extra key (tier: api on the Service but not on the Pod template) empties the slice as surely as a typo. Namespaces matter too. A Service only selects Pods in its own namespace, so a Deployment applied to default by mistake leaves the shop Service empty. Look for these patterns:
- A label renamed in the Pod template during a Helm or Kustomize refactor while the Service kept the old key.
- A canary or blue/green label (
track: stable) added to the Service selector before any Pod carries it. - A
kubectl labelhotfix on running Pods that the next rollout silently reverted.
Validate both manifests in the Kubernetes YAML linter before you apply the fix, so a quoted value or an indentation slip doesn’t create a new mismatch.
Branch 2: the Pods match but are not Ready
When the slice lists addresses with ready: false, Kubernetes is doing its job. The Pod lifecycle documentation says that a failing readiness probe takes the Pod out of every matching Service’s endpoints, and that probes keep running for the container’s whole life. A dependency outage can therefore empty a Service minutes after a healthy rollout.
Readiness gates add a second way to get stuck. A Pod with readinessGates is Ready only when all of its containers are ready and every listed condition is True. A condition missing from status.conditions counts as False. If the controller that is supposed to set the gate (often a load-balancer controller) is down or misconfigured, every Pod stays at ContainersReady and never reaches Ready. Check both:
kubectl get pod checkout-7d9c-abcde -n shop \
-o jsonpath='{range .status.conditions[*]}{.type}={.status}{"\n"}{end}'
kubectl describe pod checkout-7d9c-abcde -n shop | sed -n '/Events/,$p'
For probe-level fixes (paths, ports, timing, and dependency checks), follow the readiness probe debugging guide and the probe configuration reference. Do not “fix” this branch by loosening readiness until the probe passes. That moves the failure from an empty Service to errors returned to users.
publishNotReadyAddresses: true is the one deliberate exception. The Service API reference says controllers then treat every endpoint as ready, even when the Pods are not. Its intended use is a StatefulSet’s headless Service that publishes peer DNS records during bootstrap. On an ordinary ClusterIP Service it hides exactly the signal you are looking for.
Branch 3: endpoints exist but connections still fail
If the slice shows ready addresses and clients still fail, check ports before networking. The Service’s targetPort is a number or a name. A name is looked up in each target Pod’s container ports, and if targetPort is omitted it defaults to port. The debugging guide lists the traps: the Service port is not in spec.ports[], the number was written as the string "9376", a named port doesn’t exist on the Pod, or the protocol is wrong.
Named ports also explain a confusing rollout state. All endpoints in one slice must share the same ports, so a rollout that moves a named port from 80 to 8080 can briefly leave the Service with two slices. That is normal. Test the Pods directly on the Pod port, not the Service port:
kubectl run probe --rm -it --image=busybox:1.36 -n shop -- \
wget -qO- -T 3 http://10.244.0.5:8080/healthz
If the direct test works but the ClusterIP doesn’t, look at the proxy layer: kube-proxy logs, then rules with iptables-save or ipvsadm -ln, as described in the virtual IPs reference. A NetworkPolicy that denies ingress to the Pods produces the same symptom but leaves the endpoints intact. A DNS lookup that returns the wrong ClusterIP points to a client in the wrong namespace.
Branch 4: selectorless Services and terminating Pods
A Service with no selector never gets automatic endpoints. The Service concepts page describes this pattern for backends outside the cluster or in another namespace. You create EndpointSlices yourself and link them with the kubernetes.io/service-name label. Manually listed IPs cannot be loopback, link-local, or another Service’s ClusterIP. If a selectorless Service is empty, something that used to populate it (an operator, a sync job, or a Terraform module) has stopped. Find that owner. Don’t add a selector as a quick fix.
During a rollout or node drain, the slice may list endpoints with terminating: true. Those endpoints always report ready: false for backward compatibility. Proxies normally skip them, but if every endpoint is terminating, proxies may route to endpoints that are both serving and terminating, so in-flight traffic can drain. A Service that only goes empty during deploys usually means maxUnavailable, PodDisruptionBudgets, or preStop timing let every old Pod start terminating before a new Pod became Ready.
A ten-minute no-endpoints checklist
- List slices by label:
kubectl get endpointslices -l kubernetes.io/service-name=<svc>. Empty means branch 1 or 4; addresses listed means branch 2 or 3. - Print the Service selector and run it as a Pod label query in the same namespace. Fix the label at its source (chart or overlay), not with
kubectl label. - Read each endpoint’s
ready,serving, andterminatingconditions with thejqfilter above. - For not-ready Pods, read Pod conditions and events, and check readiness gates before you touch the probe.
- Confirm
targetPortresolves to a real container port and protocol, then test one Pod IP directly. - If the Pod test passes but the ClusterIP fails, check NetworkPolicy, then kube-proxy rules.
- Retire any
kubectl get endpointschecks in runbooks and dashboards in favor of EndpointSlices.
Frequently asked questions
Why does my Kubernetes Service show no endpoints when the Pods are Running?+
Should I use kubectl get endpoints or kubectl get endpointslices?+
What does ready: false mean in an EndpointSlice?+
Can a Service select Pods in another namespace?+
Is publishNotReadyAddresses a safe fix for an empty Service?+
Why does my Service go empty only during deployments?+
Read next
- Lint the Service and Deployment together in the Kubernetes YAML linter.
- Kubernetes readiness probe debugging
- Kubernetes 1.37 upgrade readiness checklist
Tags: #kubernetes, #services, #endpointslices, #networking, #debugging