1. Introduction

A Kubernetes Service that isn't reachable can mean a dozen different things depending on where you're connecting from: inside the cluster (pod-to-service), from a node, or from outside the cluster (NodePort, LoadBalancer, Ingress). Each access path has its own failure modes, and the troubleshooting approach differs significantly between them.

This guide focuses on the most common scenarios: a Service that returns connection refused, times out, or gets no response. For cases where DNS resolution is the issue, see Fix DNS Resolution Issues in Kubernetes. For pod-to-pod communication without a Service, see Fix Internal Service Communication Failure.

2. What Service Not Reachable Means

A Kubernetes Service is a virtual IP and port that load-balances to a set of pods. Traffic hits the ClusterIP, and kube-proxy (via iptables or IPVS) redirects it to a ready pod endpoint. If the Service has no endpoints, all connections go nowhere. If kube-proxy is broken, the iptables rules don't exist. If the pods aren't ready, they're excluded from endpoints.

3. Common Causes

4. Step-by-Step Diagnosis and Fix

Step 1: Check Service endpoints

# The single most important check
kubectl get endpoints <service-name> -n <namespace>

# ENDPOINTS shows pod IPs and ports that will receive traffic
# If <none>: the selector matches no ready pods

# Check what the selector is
kubectl get service <service-name> -n <namespace> -o yaml | grep -A 5 selector

# Check which pods match the selector
kubectl get pods -n <namespace> -l <key=value> --show-labels

# Check pod readiness (not-ready pods are excluded from endpoints)
kubectl get pods -n <namespace> -l <key=value>
# READY column should show 1/1 or N/N

Step 2: Test the Service from inside the cluster

# Run a debug pod in the same namespace
kubectl run debug --image=curlimages/curl --rm -it --restart=Never -n <namespace> --   curl -v http://<service-name>:<port>/

# Test by ClusterIP (bypasses DNS)
CLUSTER_IP=$(kubectl get service <service-name> -n <namespace> -o jsonpath='{.spec.clusterIP}')
kubectl run debug --image=curlimages/curl --rm -it --restart=Never --   curl -v http://$CLUSTER_IP:<port>/

# If ClusterIP works but name doesn't = DNS issue
# If ClusterIP fails = endpoints or kube-proxy issue

Step 3: Check targetPort configuration

# View Service port config
kubectl get service <service-name> -n <namespace> -o yaml | grep -A 10 ports

# Example of misconfiguration:
# ports:
# - port: 80
#   targetPort: 8080   <-- must match what the container ACTUALLY listens on

# Verify what port the container listens on
kubectl exec -it <pod-name> -n <namespace> -- ss -tlnp
# or: netstat -tlnp

# Fix: update Service targetPort to match
kubectl patch service <service-name> -n <namespace>   --type='json' -p='[{"op":"replace","path":"/spec/ports/0/targetPort","value":8080}]'

Step 4: Check NetworkPolicy

# List policies in both source and target namespaces
kubectl get networkpolicy -n <source-namespace>
kubectl get networkpolicy -n <target-namespace>

# A default-deny-all policy blocks all traffic unless explicitly allowed
# Allow traffic to the Service's pods:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-service-traffic
  namespace: <target-namespace>
spec:
  podSelector:
    matchLabels:
      app: <target-app>
  ingress:
  - from:
    - namespaceSelector: {}   # allow from all namespaces
    ports:
    - port: 8080
  policyTypes:
  - Ingress

Step 5: Fix external access (NodePort/LoadBalancer)

# For NodePort: confirm the port is open on the node
NODE_IP=$(kubectl get node -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
NODE_PORT=$(kubectl get service <service-name> -o jsonpath='{.spec.ports[0].nodePort}')
curl http://$NODE_IP:$NODE_PORT/

# For AWS LoadBalancer: check Security Group allows inbound on the Service port
# The node security group needs inbound on NodePort range (30000-32767) from the LB
# Check: aws ec2 describe-security-groups --group-ids sg-xxxxx

# For Ingress-based access, see:
# /fix-kubernetes-ingress-not-working.html

5. Verification Steps

# Service should have endpoints
kubectl get endpoints <service-name> -n <namespace>
# Should show pod IPs, not <none>

# Direct curl test from inside the cluster should succeed
kubectl run verify --image=curlimages/curl --rm -it --restart=Never -n <namespace> --   curl -sf http://<service-name>:<port>/health
# Expected: HTTP 200

6. Common Mistakes

7. Prevention Tips

8. FAQ

The Service has endpoints but connections still fail. What else could it be?

If endpoints exist but traffic doesn't reach the pods: (1) kube-proxy iptables rules may be stale or missing — restart kube-proxy, (2) a NetworkPolicy is blocking the specific port or source namespace, (3) the container is listening on a different port than targetPort. Test by curling the pod IP directly: if that works but the ClusterIP doesn't, it's kube-proxy or NetworkPolicy.

Service works for some pods but not others in the same namespace. Why?

Most likely NetworkPolicy — one pod has an egress policy that doesn't allow traffic to the Service's port, while others have no restrictive policy. Run kubectl get networkpolicy -n <namespace> and inspect each policy's podSelector to see which pods it applies to.

9. Summary

SymptomCauseFix
Endpoints is <none>Selector doesn't match podsFix label selector or pod labels
Endpoints exist, still times outNetworkPolicy or kube-proxyCheck NetworkPolicy; restart kube-proxy
ClusterIP works, name failsDNS resolution issueSee DNS Resolution Issues guide
NodePort external access failsFirewall / security groupOpen NodePort range on node security group
502 from LoadBalancerNo healthy backend podsCheck pod readiness; fix readiness probe

Explore More in This Category

Explore more in this category: Kubernetes guides. Browse all DevOps Compass articles or jump to: Kubernetes, AWS, CI/CD, Containers, Monitoring, Networking.