Full lesson
Explore the full explanation, examples, and visuals at your own pace.
Same Pod, two results
The same Pod answers one request and refuses another. Through kubectl port-forward, localhost:8080 returns 200 OK. But when the frontend Pod calls the Service named web on port 80, curl says connection refused. The Pod didn't change between those two requests. Only the path into it did.
$ kubectl port-forward pod/web-7d9f 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
# browser: http://localhost:8080 -> 200 OK
$ kubectl exec frontend -- curl http://web:80
curl: (7) Failed to connect to web port 80: Connection refusedThe app listens only on loopback
The Pod has two network addresses: loopback at 127.0.0.1, and its Pod IP, 10.1.2.7 on eth0. Our app is bound only to loopback. So it answers on 127.0.0.1 port 8080, but nothing listens on the Pod IP. Binding to 0.0.0.0 would cover both.
Two paths into the same Pod
Same Pod, same app, two different outcomes. Port-forward tunnels in and lands on 127.0.0.1:8080, where the app is listening, so it answers. The Service sends frontend's request to the Pod IP, 10.1.2.7:8080. Nothing listens there, so the connection is refused.
Port-forward: a tunnel ending at localhost
Follow the port-forward request. Your laptop opens a tunnel to the API server, which streams it to the kubelet on the node. The kubelet then connects to localhost port 8080 inside the Pod's network namespace. That's why an app bound only to 127.0.0.1 still answers, and the 200 OK travels back.
Service: routed to the Pod IP
Now follow the Service path. The frontend Pod calls web on port 80, and the Service routes that request to 10.1.2.7 on targetPort 8080. That's the Pod IP, not localhost. Our app is bound only to loopback, so nothing is listening there, and the connection is refused right away.
What port-forward really tells you
Port-forward is a debugging tunnel to one Pod. It skips Service routing, Ingress, and any load balancer entirely. So when it works, you've proven the app is running inside the Pod. You haven't proven the Service can reach it.
- A debugging tunnel to one Pod
- Skips Service routing, Ingress, load balancer
- Proves the app runs, not the Service
Port-forward to the web Pod returns 200 OK. What has that actually proven?
Port-forward to the web Pod returns 200 OK. What has that actually proven?
- The app is running and answering inside the Pod
- The Service routes traffic correctly to the Pod
- The Ingress and load balancer are configured correctly
Port-forward to the web Pod returns 200 OK. What has that actually proven?
The app is running and answering inside the Pod. Port-forward tunnels straight to one Pod and connects to localhost inside it. It never uses the Service, the Ingress, or a load balancer. So it shows only that the app is alive, not that traffic arriving at the Pod IP will reach it.
- The app is running and answering inside the Pod
- The Service routes traffic correctly to the Pod
- The Ingress and load balancer are configured correctly
Three things must line up
The fix starts with binding to 0.0.0.0, so the app accepts traffic on the Pod IP. The variable name depends on your framework. Then the port must match everywhere. The app listens on 8080, and targetPort must be 8080. containerPort is mostly documentation, but targetPort has to be right.
# app config (name varies by framework)
HOST=0.0.0.0
PORT=8080
---
# Deployment container
ports:
- containerPort: 8080
---
# Service web
selector:
app: web
ports:
- port: 80
targetPort: 8080Quick diagnostic path
Start with the logs and find the listen line. If it says 127.0.0.1, that's loopback only. Next, compare the containerPort to the Service targetPort. Finally, check endpoints for 10.1.2.7 on port 8080. The log wording is illustrative, since every app prints it differently.
# 1. Bind address
kubectl logs pod/web-7d9f | grep -i listen
# Listening on 127.0.0.1:8080 <- loopback only
# 2. Compare ports
kubectl get pod web-7d9f -o jsonpath='{.spec.containers[*].ports[*].containerPort}'
kubectl get svc web -o jsonpath='{.spec.ports[*].targetPort}'
# 3. Endpoints exist?
kubectl get endpoints webSimilar symptoms, different causes
A failing Service can look the same from the outside, so check endpoints first. If they're empty, the selector doesn't match the Pod labels. If endpoints exist but traffic still fails, suspect the wrong targetPort or a loopback bind. Port-forward hides all three.
Inside the Pod vs at the Pod IP
Port-forward knocks from inside the Pod. Services knock on its IP. So if the app listens only on localhost, only the tunnel gets in. Bind to 0.0.0.0, then make sure targetPort and the selector line up.




