Kubernetes Network Created: 12 Apr 2026 Updated: 12 Apr 2026

Kubernetes Endpoints

Overview

When a Service routes traffic to your pods, how does it know which pods are actually running and healthy? The answer is Endpoints. An Endpoints object is an automatically managed list of IP addresses and ports that belong to the pods matching a Service's label selector.

Think of a Service as a restaurant's phone number and the Endpoints as the list of waiters currently on shift. Customers call the phone number (Service), and the call is forwarded to one of the available waiters (a pod whose IP is in the Endpoints list). If a waiter goes on break (a pod fails its readiness check), the restaurant stops forwarding calls to that waiter until they come back.

In this article you will learn what Endpoints are, how Kubernetes creates and maintains them, how to inspect them, and what happens when a pod's labels change. We will also briefly introduce EndpointSlices, the newer mechanism designed for large-scale clusters.

Core Concepts

What Is an Endpoints Object?

An Endpoints object is a Kubernetes resource that stores a set of network addresses (IPs and ports) for the pods that a Service targets. Every time you create a Service with a label selector, Kubernetes automatically creates a matching Endpoints object with the same name. You rarely create Endpoints by hand; the system keeps them in sync for you.

Ready vs. Not-Ready Addresses

Each Endpoints object contains two lists of addresses:

  1. .addresses — Pods that are passing their readiness checks. Traffic is sent only to these addresses.
  2. .notReadyAddresses — Pods that exist but are not passing readiness checks. They are tracked but do not receive traffic.

This mechanism turns Endpoints into a built-in service-discovery tool. By watching an Endpoints object, you can see every pod's health status and IP address in real time.

Endpoints Structure

Below is a minimal Endpoints manifest that shows the two address lists and a port definition:

apiVersion: v1
kind: Endpoints
metadata:
labels:
name: demo-endpoints
subsets:
- addresses:
- ip: 10.0.0.1
notReadyAddresses:
- ip: 10.0.0.2
ports:
- port: 8080
protocol: TCP

The ports section applies to every address in the subset. If a pod at 10.0.0.1 is healthy, it appears under addresses. If the pod at 10.0.0.2 is failing its readiness probe, it appears under notReadyAddresses.

How Endpoints Are Maintained

The endpoint controller inside the Kubernetes control plane continuously watches pods and services. When a pod's labels match a Service's selector and its readiness probe passes, the controller adds the pod's IP to .addresses. When a pod is deleted, becomes unready, or its labels no longer match, the controller removes or moves the IP accordingly.

This is fully automatic. You do not need to edit the Endpoints object yourself.

Label Changes and Their Effect

Because the endpoint controller relies on label selectors, changing a pod's labels has an immediate effect. If you overwrite a pod's label so that it no longer matches the Service selector, two things happen at once:

  1. The endpoint controller removes the pod's IP from the Endpoints object because the label no longer matches.
  2. The deployment controller notices that the desired replica count is no longer met (the relabeled pod is no longer counted), so it creates a new pod to satisfy the replica count.

The relabeled pod keeps running — it is not deleted. It simply becomes an "orphan" that is no longer managed by the Deployment or included in the Endpoints. This technique is sometimes used deliberately to pull a misbehaving pod out of rotation for debugging without losing it.

EndpointSlices

For large Deployments with hundreds or thousands of pods, a single Endpoints object can become very large. Every small change (a pod restart, a readiness flip) requires the entire object to be rewritten, which can slow down the cluster.

To solve this, Kubernetes introduced EndpointSlices. An EndpointSlice holds a subset of the endpoints (by default up to 100 addresses per slice). When a pod changes, only the relevant slice is updated instead of the entire list. EndpointSlices are the default in modern Kubernetes versions and work transparently behind the scenes — Services use them automatically.

Hands-On: Kubernetes Commands

Listing Endpoints

To see all Endpoints in the current namespace:

kubectl get endpoints

Getting Endpoints for a Specific Service

To view the Endpoints object that belongs to a specific Service:

kubectl get endpoints shipping-api-service

Describing Endpoints in Detail

The describe command gives you a full view of ready addresses, not-ready addresses, and ports:

kubectl describe endpoints shipping-api-service

Watching Endpoints in Real Time

Use the -w (watch) flag to stream live changes as pods come and go:

kubectl get endpoints -w

Watching Pods in Real Time

Open a second terminal and watch pods at the same time to correlate changes:

kubectl get pods -w

Listing Pods with Wide Output

The -o wide flag adds IP addresses and node assignments, which makes it easy to match pods to endpoint addresses:

kubectl get pods -l app=shipping-api -o wide

Overwriting a Pod Label

Remove a pod from the Endpoints by changing its label (replace <pod-name> with an actual pod name from your cluster):

kubectl label pod <pod-name> app=debug --overwrite

Listing EndpointSlices

To view EndpointSlices instead of the legacy Endpoints object:

kubectl get endpointslices

Step-by-Step Example

Step 1 — Deploy the Shipping API

We will deploy a simple ASP.NET Core application with four replicas so we have multiple pod IPs to observe in the Endpoints.

apiVersion: apps/v1
kind: Deployment
metadata:
name: shipping-api
labels:
app: shipping-api
spec:
replicas: 4
selector:
matchLabels:
app: shipping-api
template:
metadata:
labels:
app: shipping-api
spec:
containers:
- name: shipping
image: mcr.microsoft.com/dotnet/aspnet:10.0
command: ["dotnet", "--info"]
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "300m"
memory: "256Mi"

Apply the Deployment:

kubectl apply -f shipping-api-deployment.yaml

Wait until all four pods are running:

kubectl get pods -l app=shipping-api -o wide

You should see four pods, each with its own IP address and assigned node.

Step 2 — Create the Service

Now create a ClusterIP Service that selects pods with the label app=shipping-api. This will automatically create an Endpoints object.

apiVersion: v1
kind: Service
metadata:
name: shipping-api-service
spec:
type: ClusterIP
selector:
app: shipping-api
ports:
- port: 80
targetPort: 8080

Apply the Service:

kubectl apply -f shipping-api-service.yaml

Step 3 — Inspect the Endpoints

As soon as the Service is created, Kubernetes generates an Endpoints object with the same name. List it:

kubectl get endpoints shipping-api-service

You should see the IP addresses of all four pods:

NAME ENDPOINTS
shipping-api-service 10.244.1.3:8080,10.244.1.4:8080,10.244.2.5:8080 + 1 more...

For the full picture, use describe:

kubectl describe endpoints shipping-api-service

Example output:

Name: shipping-api-service
Namespace: default
Labels: app=shipping-api
Subsets:
Addresses: 10.244.1.3,10.244.1.4,10.244.2.5,10.244.3.6
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
<unset> 8080 TCP

All four addresses are in the Addresses list because their readiness probes are passing. NotReadyAddresses is empty.

Step 4 — Open Watch Terminals

To see what happens next in real time, open two separate terminals:

Terminal 1 — Watch pods:

kubectl get pods -w

Terminal 2 — Watch endpoints:

kubectl get endpoints -w

Step 5 — Remove a Pod from the Endpoints via Label Change

Pick one of the running pod names from the earlier kubectl get pods output. Then overwrite its app label so it no longer matches the Service selector:

kubectl label pod <pod-name> app=debug --overwrite

Watch your two terminals. You will observe the following sequence:

  1. The endpoint controller removes the relabeled pod's IP from the Endpoints object.
  2. The deployment controller detects that only three pods match the selector, but the desired replica count is four. It immediately schedules a new pod.
  3. The new pod starts, passes its readiness probe, and the endpoint controller adds its IP to the Endpoints.

Step 6 — Verify the New State

List all pods (including the orphan):

kubectl get pods -o wide

You will now see five pods: the original four (with one relabeled) plus one newly created pod. The relabeled pod is still running but is no longer part of the Deployment or the Endpoints.

Verify the Endpoints again:

kubectl describe endpoints shipping-api-service

The output now shows four IP addresses, but the relabeled pod's old IP has been replaced by the new pod's IP.

Step 7 — Clean Up

Remove all the resources you created:

kubectl delete -f shipping-api-deployment.yaml
kubectl delete -f shipping-api-service.yaml

Don't forget to delete the orphaned pod if it is still running:

kubectl delete pod <relabeled-pod-name>

Summary

  1. Endpoints are automatically created and maintained when you create a Service with a label selector. They list the IP addresses and ports of all matching pods.
  2. Each Endpoints object has two address lists: addresses (healthy, ready pods) and notReadyAddresses (pods that exist but are not passing readiness checks).
  3. The endpoint controller watches for pod and label changes and updates the Endpoints object in real time.
  4. Changing a pod's label removes it from the Endpoints and triggers the Deployment to create a replacement, leaving the relabeled pod running as an "orphan."
  5. For large-scale clusters, EndpointSlices replace the single Endpoints object with smaller chunks (up to 100 addresses each) to improve performance.
  6. Use kubectl get endpoints, kubectl describe endpoints, and kubectl get endpoints -w to inspect and monitor Endpoints.


Share this lesson: