How to Fix a Kubernetes Service targetPort Mismatch
Pods are Running, endpoints are populated, but every request to the Service gets connection refused. The cause is almost always a targetPort that doesn't match the port the container actually listens on. Here's how to diagnose and fix it.
Why connection refused happens even when pods are Running
Kubernetes validates that a Service has endpoints - pods selected by the label selector -
but it does NOT check that targetPort matches a port the container actually listens on.
Traffic is forwarded to whatever targetPort says, and if nothing is listening there,
you get connection refused.
A pod can show Running and 1/1 Ready while all traffic to its Service silently fails.
Diagnose: compare targetPort to containerPort
First confirm that endpoints exist (selected pods are present):
kubectl get endpoints web
If endpoints are populated, the selector is fine. Now read the Service and Deployment side by side:
# What port is the Service routing to?
kubectl get service web -o yaml | grep targetPort
# What port is the container actually listening on?
kubectl describe deployment web | grep containerPort
A mismatch like targetPort: 8080 with containerPort: 80 is the bug. The Service
is sending traffic to port 8080 but the container's process only binds port 80.
Fix: align targetPort to containerPort
In your Deployment manifest, set targetPort in the Service spec to match containerPort
in the container spec:
# deployment.yaml (Service portion)
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80 # must match containerPort below
---
# deployment.yaml (Deployment portion)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
template:
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80 # what the process actually binds
Apply and verify:
kubectl apply -f deployment.yaml
kubectl rollout status deployment/web
kubectl get endpoints web # should show IP:80, not IP:8080
Better: name the port so the mismatch is impossible
Reference ports by name instead of number. Define a name on the container port
and use that name as targetPort in the Service:
# Service
ports:
- port: 80
targetPort: http # name reference - always tracks the container port
# Container
ports:
- name: http
containerPort: 80
With named ports, changing containerPort automatically fixes targetPort with no
cross-field hunting. This is the recommended pattern for any non-trivial manifest.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Comparing Service targetPort to Deployment containerPort to find a mismatch
- Applying a fixed manifest and verifying endpoints update correctly
- Using named ports to make targetPort mismatches impossible
FAQ
Why does my Kubernetes Service get connection refused even though pods are Running?
The most common cause is a targetPort mismatch - the Service routes to a port number the container isn't listening on. Pods show Running because Kubernetes only checks that the container process started, not that the right port is bound. Compare targetPort in the Service to containerPort in the Deployment.
How do I check if my Kubernetes Service targetPort is correct?
Run kubectl get service <name> -o yaml | grep targetPort and kubectl describe deployment <name> | grep containerPort. They must match. You can also check kubectl get endpoints <name> - populated endpoints mean the selector works, but traffic can still fail if targetPort points nowhere.
What is the difference between port and targetPort in a Kubernetes Service?
port is the port the Service listens on (what callers connect to). targetPort is the port on the pod the Service forwards traffic to - it must match the containerPort the application actually binds. They are often different values, for example port 80 -> targetPort 3000 for a Node app.
What are the types of Kubernetes Services?
Four: ClusterIP (internal-only, the default), NodePort (a port on every node), LoadBalancer (an external load balancer), and ExternalName (maps to a DNS name). Most internal traffic uses ClusterIP.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →