Kubernetes

Service has no endpoints

Your Kubernetes Service accepts connections and forwards them nowhere. There are exactly two causes — a selector that matches nothing, or Pods that are not Ready.

The error

$ kubectl get endpointslices -l kubernetes.io/service-name=payments-api
NAME                 ADDRESSTYPE   PORTS     ENDPOINTS
payments-api-x7k2q   IPv4          <unset>   <unset>

$ curl payments-api
curl: (7) Failed to connect to payments-api port 80: Connection refused

A Service with no endpoints is a dead end. It has a working DNS name and a working virtual IP, and nothing behind it — so connections are accepted and then fail.

The good news: there are exactly two causes, and five minutes of checking distinguishes them.

Confirm the diagnosis

kubectl get endpointslices -l kubernetes.io/service-name=payments-api
NAME                 ADDRESSTYPE   PORTS     ENDPOINTS
payments-api-x7k2q   IPv4          <unset>   <unset>

Empty ENDPOINTS confirms it. The older equivalent still works:

kubectl get endpoints payments-api
# NAME           ENDPOINTS   AGE
# payments-api   <none>      12m

kubectl describe service shows the same thing:

Selector:   app=payments
Endpoints:  <none>

Now: is it the selector, or readiness?

Cause 1 — The selector matches no Pods

A Service finds its backends by label selector, and Kubernetes never validates that the selector matches anything. A Service with a typo is created successfully, reports healthy, and routes nowhere.

Compare the two directly:

kubectl get svc payments-api -o jsonpath='{.spec.selector}{"\n"}'
# {"app":"payments"}

kubectl get pods --show-labels
# NAME                     READY   STATUS    LABELS
# payments-api-7d4f-x8k2   1/1     Running   app=payments-api,pod-template-hash=7d4f

There it is — the Service selects app=payments, the Pods carry app=payments-api. Close enough to miss on a read-through, and completely non-matching to Kubernetes.

Test the selector directly, which is the fastest confirmation:

kubectl get pods -l app=payments
# No resources found in payments namespace.

If that returns nothing, you have found the problem. Fix whichever side is wrong — usually the Service, since changing Pod labels means a Deployment rollout.

The namespace variant

A Service only selects Pods in its own namespace. Selectors do not cross namespaces, and there is no way to make them.

kubectl get svc payments-api -n payments -o jsonpath='{.metadata.namespace}{"\n"}'
kubectl get pods -l app=payments-api -A

If the Pods are in a different namespace from the Service, no selector will ever match. Move the Service, or use an ExternalName Service pointing at the real one's FQDN.

Cause 2 — The Pods match but are not Ready

Only Ready Pods become endpoints. That is deliberate and is the entire point of a readiness probe — but it means a misconfigured probe silently empties your Service while the Pods look fine at a glance.

kubectl get pods -l app=payments-api
NAME                     READY   STATUS    RESTARTS   AGE
payments-api-7d4f-x8k2   0/1     Running   0          6m
payments-api-7d4f-m3p9   0/1     Running   0          6m

STATUS: Running with READY: 0/1 is the signature. The container is up; the probe is failing.

kubectl describe pod payments-api-7d4f-x8k2 | grep -A5 Events
Warning  Unhealthy  Readiness probe failed: HTTP probe failed with statuscode: 404

The probe message names the cause. In rough order of frequency:

Probe messageCause
statuscode: 404Wrong path — the endpoint does not exist
connection refusedWrong port, or the app is not listening yet
statuscode: 401 / 403The health path sits behind authentication
context deadline exceededtimeoutSeconds too low, default is 1 second
statuscode: 302An auth redirect, and only 200–399 counts as success

Two fixes worth knowing beyond the obvious.

A slow-starting application needs a startup probe, not a bigger initialDelaySeconds:

startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  periodSeconds: 10
  failureThreshold: 30     # up to 300s to boot
readinessProbe:
  httpGet: { path: /healthz, port: 8080 }
  periodSeconds: 5
  timeoutSeconds: 3        # the 1s default is aggressive

Test the endpoint from inside the Pod, which settles most probe arguments in one command:

kubectl exec -it payments-api-7d4f-x8k2 -- wget -qO- localhost:8080/healthz

If that works and the probe fails, the probe's path or port is wrong. If it fails too, the application is the problem.

The port mismatch that looks like this

Endpoints can be populated and traffic still fail, if targetPort does not match what the container listens on:

ports:
  - port: 80          # what clients dial
    targetPort: 8080  # must match the container's actual port
kubectl get svc payments-api -o jsonpath='{.spec.ports}{"\n"}'
kubectl get pod <pod> -o jsonpath='{.spec.containers[0].ports}{"\n"}'

The symptom differs: endpoints exist but connections are refused. Using a named port avoids it entirely, since the name survives a port change:

# pod
ports:
  - name: http
    containerPort: 8080
# service
targetPort: http

Headless Services look empty and are not

A Service with clusterIP: None shows no cluster IP by design, and kubectl get svc displays None in that column. It still has endpoints, and DNS returns the Pod IPs directly. Check the EndpointSlice rather than the Service's cluster IP before concluding anything is wrong.

A checklist

  1. kubectl get endpointslices -l kubernetes.io/service-name=<svc> — confirm the list is empty.
  2. kubectl get pods -l <the service's selector> — does it return anything?
  3. Nothing returned → selector mismatch. Compare --show-labels against the Service's selector.
  4. Also confirm the Pods are in the same namespace as the Service.
  5. Pods returned but READY 0/1 → readiness probe. Read the event message.
  6. Test the probe path from inside the Pod with kubectl exec.
  7. Endpoints exist but connections refused → check targetPort against the container's port.

Frequently Asked Questions

Why does my Kubernetes Service have no endpoints?

Two causes only. Either no Pod carries labels matching the Service's selector, or matching Pods exist but are not passing their readiness probe. Distinguish them by running kubectl get pods -l <selector> with the Service's own selector — if that returns nothing it is a label mismatch, and if it returns Pods showing READY 0/1 it is readiness. Kubernetes never validates that a selector matches anything, so a Service with a typo is created happily and routes nowhere.

Why is my pod Running but not Ready?

The container process started but its readiness probe is failing, so Kubernetes deliberately withholds traffic. kubectl describe pod shows the probe's failure message in the events, which usually names the cause directly — a 404 means the wrong path, connection refused means the wrong port or an application still starting, and context deadline exceeded means the probe timed out, which is easy with the default timeoutSeconds of 1. Test the endpoint from inside the Pod with kubectl exec to confirm which side is wrong.

Can a Kubernetes Service select Pods in another namespace?

No. Selectors only match Pods in the Service's own namespace, and there is no way to widen that. If your Service and Pods are in different namespaces, no selector will ever populate the endpoints. Either move the Service into the Pods' namespace, or create an ExternalName Service that aliases the real Service's fully qualified name — payments-api.payments.svc.cluster.local — which is the supported way to reference across namespaces.

Why do I get connection refused even though endpoints exist?

Almost certainly a targetPort mismatch. The Service is forwarding to a port the container is not listening on, so the connection reaches the Pod and is refused. Compare kubectl get svc <name> -o jsonpath='{.spec.ports}' against the container's actual port. Using a named port — declaring name: http on the container and targetPort: http on the Service — removes this class of error, since the name stays correct even if the number changes.

How does a readiness probe affect Service endpoints?

Only Ready Pods are included as endpoints, which is the mechanism that makes rolling updates safe — traffic is withheld until a new Pod can actually serve. The consequence is that a misconfigured readiness probe empties the Service entirely while the Pods appear to be running normally. If you define no readiness probe at all, Kubernetes treats a started container as Ready immediately, which avoids this failure but reintroduces the one it was meant to prevent.

What is the difference between Endpoints and EndpointSlices?

EndpointSlices are the modern replacement, splitting a Service's backends across multiple smaller objects so a Service with thousands of Pods does not require one enormous object to be updated on every change. The legacy Endpoints object is still maintained for compatibility, so kubectl get endpoints <service> works and shows the same information. New tooling should read EndpointSlices, but for debugging either is fine — both show an empty list when the Service has no backends.

Learn the underlying concept

Other Kubernetes errors