Your Kubernetes pods are running. Your Service exists. The application works when tested directly inside a container, yet requests through the Service fail.

Sound familiar?

This kind of problem can be frustrating because several components must work together: the application, Pod labels, Service selectors, target ports, networking, and sometimes cluster DNS.

Instead of changing random configurations, let's troubleshoot the request path systematically.

The scenario

Imagine we have an application called api running in Kubernetes.

The pods appear healthy:

kubectl get pods -n production 

The Service also exists:

kubectl get svc api -n production 

But requests to the Service fail with a timeout, connection refusal, or another unexpected response.

These errors can have different causes. The objective is to identify where the request stops working.

Step 1: Confirm that the Service exists

Inspect the Service configuration:

kubectl describe svc api -n production 

Check the following fields:

  • Selector
  • Port
  • TargetPort
  • Endpoints or endpoint information, depending on the Kubernetes version and output

For example, a Service might contain:

apiVersion: v1 kind: Service metadata: name: api namespace: production spec: selector: app: api ports: - name: http port: 80 targetPort: 8080 type: ClusterIP 

This configuration means the Service accepts traffic on port 80 and forwards it to port 8080 on matching pods.

The application must actually listen on the target port. Declaring targetPort: 8080 does not make the application listen on that port.

Step 2: Check whether the Service has endpoints

This is one of the first checks I would perform.

kubectl get endpointslices \ -n production \ -l kubernetes.io/service-name=api 

You can also inspect the legacy Endpoints resource:

kubectl get endpoints api -n production 

If no usable endpoints appear, investigate whether the Service selector matches the intended pods and whether those pods are ready.

For example, the Service selects:

selector: app: api 

But the pods have these labels:

labels: app: backend 

The selector and pod labels do not match.

Check the actual pod labels:

kubectl get pods -n production --show-labels 

Compare them with the Service selector:

kubectl get svc api -n production -o yaml 

Correct the mismatch in your source manifests or Helm values, then deploy through your normal change process.

Important: A Service with no ready backends does not necessarily indicate a networking failure. First establish whether there are eligible endpoints.

Step 3: Verify the application is listening on the expected port

Suppose the Service forwards traffic to port 8080, but the application listens only on 3000.

The Service may exist and its selector may be correct, yet requests still fail.

Inspect the workload configuration:

kubectl get deployment api -n production -o yaml 

Review the container configuration, environment variables, application settings, and declared container ports.

Remember that containerPort is descriptive metadata; it does not force the process to listen on that port.

Where the image provides suitable tools, you can inspect listening sockets inside the container:

kubectl exec -n production <pod-name> -- ss -lnt 

The command requires ss to be installed in the container. Minimal production images often do not include troubleshooting utilities, so use an approved debugging container or ephemeral debugging workflow when necessary.

Also consider the address on which the process listens. An application bound only to 127.0.0.1 may not accept connections through the Pod IP.

Step 4: Test the application independently

Before investigating the entire network path, establish whether the application responds locally.

If the container includes a suitable HTTP client, you might test it with:

kubectl exec -n production <pod-name> -- \ curl -v http://127.0.0.1:8080/health 

Replace the port and path with the values appropriate for your application.

If curl is unavailable, use another approved debugging method.

Interpret the result carefully:

  • A successful response confirms that the local endpoint responds.
  • Connection refusal suggests there may be no listener at that address and port.
  • An HTTP error may indicate an application, route, or dependency problem.
  • A timeout may indicate a slow application or another connectivity issue.

A successful localhost request does not prove that the application is reachable through its Pod IP or Service IP.

That distinction is important.

Step 5: Test connectivity from another pod

Next, test from a client running inside the cluster.

If an approved diagnostic pod with curl is already available, use it. Otherwise, create a temporary debugging pod in a suitable namespace, following your cluster's security and image-pull policies.

For example:

kubectl run net-debug \ -n production \ --image=curlimages/curl \ --restart=Never \ --command -- sleep 3600 

Wait for the pod to become ready:

kubectl get pod net-debug -n production -w 

Then test the Service:

kubectl exec -n production net-debug -- \ curl -v --connect-timeout 5 http://api:80/ 

This example assumes the diagnostic image can be pulled and that the pod is permitted to run.

If the client and Service are in the same namespace, api is normally sufficient as the Service DNS name.

For a Service in another namespace, use a namespace-qualified name:

api.<namespace>.svc.cluster.local 

Replace <namespace> with the actual namespace and account for any custom cluster DNS suffix.

If the test fails, note the exact failure. A DNS resolution error, connection refusal, and timeout are not interchangeable symptoms.

Clean up the temporary pod when finished:

kubectl delete pod net-debug -n production 

Do not use this approach in restricted production environments without following your team's debugging and access policies.

Step 6: Separate DNS problems from connectivity problems

If the request uses a Service name, check whether that name resolves.

From an appropriate diagnostic pod:

nslookup api 

If the client is in another namespace, test the namespace-qualified name:

nslookup api.production 

If name resolution fails, inspect the pod's DNS configuration and the cluster DNS components.

Useful checks include:

kubectl get pods -n kube-system kubectl get svc -n kube-system kubectl get endpointslices -n kube-system 

These commands provide a starting point. Narrow the investigation to the DNS Service and its corresponding EndpointSlices rather than assuming every component in kube-system is relevant.

If DNS resolves successfully, test the Service by its ClusterIP from an appropriate in-cluster client:

kubectl get svc api -n production 

Then use the displayed ClusterIP and Service port in a request from the diagnostic pod.

If the ClusterIP works but the Service name does not, focus on DNS and name resolution. If DNS works but the ClusterIP request fails, continue investigating the Service data path, endpoints, ports, policies, and network implementation.

Step 7: Check network policies and the wider network path

If the labels, endpoints, application port, and DNS configuration appear correct, investigate network controls.

Review policies in both the client and application namespaces:

kubectl get networkpolicy -A 

Where applicable, inspect cloud firewall rules, security groups, CNI configuration, routing, and the implementation responsible for Service traffic.

Also review application logs:

kubectl logs deployment/api -n production --tail=100 

Look for evidence that requests are reaching the application.

A network policy, for example, may prevent traffic from a client pod from reaching an otherwise healthy application. However, the absence of a NetworkPolicy resource does not prove that every form of network filtering is absent.

Avoid changing firewall rules or disabling policies as a first response. Identify the denied traffic and make the smallest approved change necessary.

A practical troubleshooting decision tree

Use the evidence you collected to choose the next step:

Finding Next investigation
Service does not exist Namespace, resource name, and deployment
No usable endpoints Selector, pod labels, readiness, and EndpointSlices
Local application request fails Process, application logs, address, and port
Local request works but Pod IP fails Listener address and pod networking
Service name does not resolve DNS configuration and cluster DNS
DNS works but Service request fails Service port, target port, endpoints, and network path
Requests reach the app but return errors Application logic, routing, dependencies, and logs

This table is a guide rather than a guarantee: multiple faults can occur at the same time.

What I would check first during an incident

My initial sequence would be:

  1. kubectl describe svc api -n production
  2. kubectl get endpointslices -n production -l kubernetes.io/service-name=api
  3. kubectl get pods -n production --show-labels
  4. Test the application endpoint directly.
  5. Test the Service from an in-cluster client.
  6. Investigate DNS, network policies, and the cluster networking implementation as the evidence requires.

This approach moves from the Service configuration toward the application and network path without immediately changing production resources.

Final thoughts

When a Kubernetes Service stops working, the existence of the Service and the health of the pods are only two pieces of the puzzle.

The real question is: At which point in the request path does communication fail?

Verify the selector, inspect the endpoints, confirm the listening port, test from inside the cluster, and use the results to guide the next investigation.

The goal is not to run every possible command. It is to find the smallest piece of evidence that narrows the problem.

*Official references
*