Skip to main content
orphan · kubernetes

Services routing to nothing

resource types
1
rule IDs covered
3
severity
medium

What does ZopNight detect here?

A Service whose EndpointSlice is empty has no pods behind it, so every request to it fails. ZopNight reads the EndpointSlice rather than the Service, because a selector can look correct and still match 0 pods, which is the failure this catches.

Signal and threshold

How ZopNight evaluates Services routing to nothing.
Field Value
Rule IDsRC-1742 · RC-1842 · RC-1942
Categoryorphan
Severitymedium
Metricnone — pure configuration read
Sourceinternal/rules/k8s/endpoint_empty.go

The Service looks fine; the EndpointSlice is empty

A Kubernetes Service is a selector plus a stable address. It does not verify that anything matches. Apply a Service with a typo in one label and Kubernetes accepts it happily: the object is valid, the ClusterIP is allocated, DNS resolves.

What is missing is the EndpointSlice: the list of pod IPs the Service actually routes to. Reading the Service tells you what it intends to match. Reading the EndpointSlice tells you what it found, which is why this rule looks there.

What callers experience

Connection refused, immediately, on a hostname that resolves. Not a timeout, not a 503 from a proxy. The failure is at the TCP level, and it looks like a network problem rather than a configuration one.

Teams typically debug DNS first, because the name resolves and the address exists, and only later discover the Service was matching nothing all along.

The usual causes

Label drift. The Deployment’s pod template labels changed and the Service selector did not. Both objects are individually valid.

Namespace mismatch. A Service only selects pods in its own namespace. Copying a manifest between namespaces breaks the pairing silently.

Zero ready pods. The selector is correct but every pod is failing readiness, so none are added to the EndpointSlice. Here the Service is not the problem. It is reporting one accurately.

That third case is worth separating before changing anything: an empty EndpointSlice may be a symptom rather than a misconfiguration.

Empty EndpointSlices, then selector versus labels

Terminal window
kubectl get endpointslices -A -o json | jq -r '
.items[] | select((.endpoints // []) | length == 0)
| "\(.metadata.namespace)/\(.metadata.labels["kubernetes.io/service-name"])"'

Then compare the selector against the actual pod labels:

Terminal window
kubectl get svc <name> -n <ns> -o jsonpath='{.spec.selector}'
kubectl get pods -n <ns> --show-labels

The cost consequence

A ClusterIP Service costs nothing, so an empty one is free. A LoadBalancer Service is not. It provisions a real cloud load balancer that bills continuously whether or not any pod sits behind it. Empty LoadBalancer Services are the ones worth finding first.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·