What "Service has no endpoints" means
A Kubernetes Service is a stable virtual IP that load-balances to a set of
backing pods. The control plane resolves that set continuously: the
endpoint slice controller
"automatically creates EndpointSlices for any Kubernetes Service that has a
selector specified," and those slices "include references to all the Pods that
match the Service selector." If that set is empty, kubectl get endpoints
prints <none>, the Service IP has nothing to forward to, and clients get
connection refused or a timeout.
Empty endpoints almost always reduce to one of four mechanisms: the selector matches no pod labels, the matching pods are not Ready (so they are excluded), the Service targetPort does not match the container's port, or no pods are running. The fix is different for each, so diagnose first.
Diagnose it
Start at the endpoints, then walk back to the cause.
# Does the Service have endpoints at all?
kubectl get endpoints <service> -n <namespace>
# ENDPOINTS = <none> -> empty set; keep going
# The modern source of truth (Endpoints is the legacy view)
kubectl get endpointslices -l kubernetes.io/service-name=<service> -n <namespace>
# What does the Service select, and on what ports?
kubectl get svc <service> -n <namespace> -o yaml # read spec.selector, spec.ports
# Do any pods carry those exact labels, and are they Ready?
kubectl get pods -n <namespace> --show-labels
kubectl get pods -n <namespace> -l <key>=<value> # use the Service's selector here
Per the Kubernetes Debug Services
guide, the checkpoints are exactly: does the Service have any EndpointSlices,
does the selector match the pod labels, are the pods Ready, and does the
targetPort match the port the application listens on. Each maps to one section
below.
Cause 1: selector does not match any pod labels
The Service spec.selector is matched against pod labels verbatim. A typo, a
wrong key, or a label that drifted (for example app: web on the Service but
app.kubernetes.io/name: web on the pods) means zero pods match, so the
controller writes an empty slice. The Kubernetes
Connecting Applications to Services
tutorial states the selector "will be evaluated continuously and the results
will be POSTed to an EndpointSlice that is connected to the Service."
Diagnose by comparing the two sets directly:
# What the Service asks for
kubectl get svc <service> -n <namespace> -o jsonpath='{.spec.selector}'
# What the pods actually have
kubectl get pods -n <namespace> --show-labels
# The decisive test: select pods with the Service's selector
kubectl get pods -n <namespace> -l app=web,tier=frontend
# returns "No resources found" -> the selector matches nothing
Fix by making one side match the other. Edit the Service selector to the labels the pods really carry, or relabel the workload. Selectors are AND-ed, so every key/value in the selector must be present on the pod:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web # must equal a label on the pods, key and value
ports:
- port: 80
targetPort: 8080
Blast radius: editing a live Service selector reroutes all traffic the instant
it is applied — if the new selector matches a different (or empty) pod set, you
cut over or black-hole production in one kubectl apply. Confirm the target pod
set with kubectl get pods -l <new-selector> before applying.
A related trap: a Service with no selector at all. The docs note that "this
Service has no selector, the corresponding EndpointSlice objects are not created
automatically" — you must manage the EndpointSlice yourself. If you expected
selector-based behavior, the missing selector field is the bug.
Cause 2: pods are not Ready (failing readiness probe)
This is the cause that fools people, because kubectl get pods shows Running.
Running is not Ready. The
EndpointSlices
docs define an endpoint's ready condition as "a shortcut for checking serving
and not terminating," where serving "maps to the Pod's Ready condition." The
Pod lifecycle
docs are blunt: if a readiness probe fails, "the Pod's IP address is removed from
Service endpoints and EndpointSlices," even though the container keeps running. A
failing readiness probe does not restart the container — it pulls the pod out of
load balancing.
Diagnose with the READY column and the pod's conditions:
# READY shows ready/total containers; 0/1 means not Ready
kubectl get pods -n <namespace> -l app=web
# Why it is not Ready: look for Readiness probe failed events
kubectl describe pod <pod> -n <namespace>
# Conditions: Ready False ; Events: "Readiness probe failed: HTTP probe failed with statuscode: 503"
Fix the underlying readiness signal, not the probe threshold, unless the probe itself is wrong. Common real causes: the app needs longer to warm up (add a startup probe rather than loosening the readiness probe), the probe path returns non-2xx, or the probe targets the wrong port. A correct readiness probe points at a port the container actually serves:
readinessProbe:
httpGet:
path: /healthz
port: 8080 # a port the container listens on
initialDelaySeconds: 5
periodSeconds: 10
Tradeoff: there is an escape hatch, spec.publishNotReadyAddresses: true, which
per the EndpointSlices docs makes the ready condition "always be true." That
publishes not-ready pods as endpoints — appropriate for headless StatefulSet peer
discovery, dangerous for a normal load-balanced Service because it routes traffic
to pods that declared themselves unable to serve. Do not set it to silence a
readiness failure.
Cause 3: containerPort / targetPort mismatch
Here the EndpointSlice may not be empty — it can list addresses — but the port is
wrong, so connections are refused. The Service targetPort is "the port the
container accepts traffic on," and per the docs "by default and for convenience,
the targetPort is set to the same value as the port field." If your container
listens on 8080 but the Service port: 80 has no targetPort, it defaults to 80
and forwards to a closed port.
Diagnose by comparing the Service targetPort against what the container serves:
kubectl get svc <service> -n <namespace> \
-o jsonpath='{.spec.ports[*].targetPort}' # e.g. 80
# What the container declares
kubectl get pod <pod> -n <namespace> \
-o jsonpath='{.spec.containers[*].ports[*].containerPort}' # e.g. 8080
# Endpoints exist but on the wrong port? this confirms it
kubectl get endpointslices -l kubernetes.io/service-name=<service> -n <namespace> -o yaml
Fix by setting targetPort to the port the application listens on. A named port
is more robust than a number, because it survives the container changing its port:
# pod / deployment
ports:
- name: http
containerPort: 8080
---
# service
ports:
- port: 80
targetPort: http # resolves to containerPort 8080 by name
Note that containerPort is documentation/metadata — a container listens on
whatever port its process binds, regardless of containerPort. So also confirm
the process is actually bound to that port (kubectl exec plus a local check),
not just that the manifest says so.
Cause 4: no pods running at all
If the workload has zero pods — scaled to 0, every replica crash-looping, or the ReplicaSet failing to create pods — there is nothing for the controller to reference, so the slice is empty. This often coincides with a separate failure that is the real story.
Diagnose by counting and inspecting the workload:
kubectl get deploy,rs,pods -n <namespace> -l app=web
# DESIRED 3 / CURRENT 0, or no pods listed
kubectl describe deploy <deployment> -n <namespace> # replicas, rollout status
kubectl get events -n <namespace> --sort-by=.lastTimestamp | tail -20
Fix depends on why the pod count is zero:
- Scaled to zero —
kubectl scale deploy/<name> --replicas=3 -n <namespace>. - Pods stuck Pending — the scheduler cannot place them; see pod-pending for the per-node reason.
- Pods crash-looping — they start, fail, and never reach Ready; see crashloopbackoff.
- ReplicaSet cannot create pods — quota, admission webhook, or PodSecurity rejection; read the events on the ReplicaSet.
Blast radius is usually low for the Service itself (it is already serving nothing), but the underlying cause may be actively rolling back a deployment, so treat it as a live incident, not a config tweak.
Recommended order of attack
Work the cheapest, highest-yield checks first:
kubectl get endpoints <service>— confirm it is genuinely<none>.kubectl get pods -l <service-selector> --show-labels— this one command distinguishes Cause 1 (no pods returned), Cause 4 (no pods exist), and Cause 2 (pods returned but READY is0/1).- If pods are Ready and selected but the Service still refuses connections,
you are in Cause 3 — compare
targetPortto the listening port.
This sequence is intentional: step 2 collapses three of the four causes into one observation, so you rarely need all four investigations.
Validate the fix
# Endpoints should now list pod IPs and ports
kubectl get endpoints <service> -n <namespace>
# ENDPOINTS 10.244.2.5:8080,10.244.3.4:8080
kubectl get endpointslices -l kubernetes.io/service-name=<service> -n <namespace>
# ADDRESSTYPE IPv4 PORTS 8080 ENDPOINTS 10.244.2.5,10.244.3.4
# End-to-end from inside the cluster
kubectl run probe --rm -it --image=busybox:1.36 --restart=Never -n <namespace> \
-- wget -qO- http://<service>.<namespace>.svc.cluster.local:80
If endpoints are now correct but the name still will not resolve, the problem has moved to DNS, not endpoints — a different failure mode worth ruling out separately.
Prevention
- Use named ports end to end (
containerPort: namereferenced by ServicetargetPort) so a port change in the container does not silently break the Service. - Treat readiness probes as a contract: the probe must check exactly what "ready to serve" means for that app, and CI should catch a probe pointed at the wrong port.
- Pin selectors to the recommended labels (app.kubernetes.io/name etc.) consistently across Service and workload, so drift between the two is obvious.
- Alert on empty endpoints for any Service that should always have backends — a Service with zero ready endpoints is a leading indicator of an outage before clients start failing.
How Intellira diagnoses this
Intellira reads the Service spec, the EndpointSlices, and the matching pods'
labels and Ready conditions read-only, then reports which of the four mechanisms
is in play: selector that matches no labels, selected pods that are not Ready
(with the readiness-probe event quoted), a targetPort that does not match the
container's listening port, or a workload at zero pods. It correlates an empty
slice with the change that caused it — a relabel, a probe edit, a port change, or
a scale/rollback — and cites the evidence rather than guessing.
Sources
- Service (concept) — selector, targetPort default, publishNotReadyAddresses
- Connecting applications with Services
- Debug Services
- EndpointSlices — ready/serving/terminating conditions
- Pod lifecycle — readiness probe removes pod from endpoints
- Labels and selectors
By Intellira Engineering. AI-assisted draft, reviewed by the Intellira engineering team; claims cited inline; last verified 2026-06-02.