Skip to content

Services, Port-Forward & Expose — Full Reference

Reference: https://kubernetes.io/docs/concepts/services-networking/service/

See also: nslookup.md.md for DNS, nodeport.md.md for NodePort, clusterIP.md.md for ClusterIP internals.


The Exercise — What Went Wrong

Expose nginx-pod internally as nginx-service, then use port-forward to curl the nginx welcome page.

The raw notes mistake: --port=8080 was used to expose, but nginx listens on port 80. So the service had targetPort: 8080 pointing at a port nothing was listening on.

The habit that prevents this:

kubectl describe pod nginx-pod | grep Port
# Port: 80/TCP

Always check the pod's actual container port before exposing. That value is your targetPort.


The Fix — Clean Sequence

# 1. Check what port the pod is actually on
kubectl describe pod nginx-pod | grep Port

# 2. Expose with the correct port
kubectl expose pod nginx-pod --port=80 --name=nginx-service

# 3. Port-forward in the background
kubectl port-forward service/nginx-service 9090:80 &

# 4. Curl it
curl localhost:9090

Port Confusion — The Full Table

Name Where What it is
containerPort Pod spec Port the app inside the container listens on. Informational only.
targetPort Service spec Port traffic is forwarded TO on the container. Must match what's actually listening.
port Service spec Port the Service itself listens on. What other pods use to reach it.
nodePort Service spec (NodePort only) Port opened on every node for external access.
local port port-forward Port on YOUR machine. What you curl locally.

For the nginx exercise:

curl localhost:9090
    ↓ port-forward
service:80          (--port=80 on expose)
    ↓ service routes
pod:80              (targetPort defaults to port value)
nginx container     (actually listening on 80)

Why it broke: --port=8080 set both port and targetPort to 8080. Service routed to port 8080 on the pod. Nothing was listening on 8080. Connection refused.


kubectl expose — Full Breakdown

kubectl expose pod nginx-pod --port=80 --name=nginx-service
Part What it does
pod nginx-pod Resource to expose
--port=80 Service port AND targetPort (both default to this when --target-port is omitted)
--name=nginx-service Name of the Service object

With explicit targetPort (when app port ≠ service port):

kubectl expose pod nginx-pod --port=80 --target-port=8080 --name=nginx-service
Service listens on 80, routes to container port 8080.

All expose options:

kubectl expose pod <name> --port=80 --name=svc-name                 # ClusterIP (default)
kubectl expose pod <name> --port=80 --type=NodePort                  # NodePort
kubectl expose deployment <name> --port=80 --target-port=8080        # from deployment
kubectl expose pod <name> --port=80 --dry-run=client -o yaml         # generate YAML only


kubectl port-forward

port-forward tunnels a local port on your machine to a port on a pod or service inside the cluster. Traffic doesn't go through the Service's load balancing — it goes directly.

kubectl port-forward service/nginx-service 9090:80
#                                           ↑    ↑
#                                     local:9090  service:80
kubectl port-forward pod/nginx-pod 9090:80           # directly to pod
kubectl port-forward service/nginx-service 9090:80   # to service (preferred)
kubectl port-forward deployment/nginx-dep 9090:80    # to deployment (picks one pod)

& — run in background:

kubectl port-forward service/nginx-service 9090:80 &

Without &, port-forward blocks the terminal — you can't type anything else. & puts it in the background so you can curl immediately. To stop it: kill %1 or fg then Ctrl+C.

When port-forward is useful: - Testing a service without exposing it externally - Accessing admin UIs (Kubernetes dashboard, Prometheus, Grafana) locally - Debugging — connect directly to the pod bypassing the service

Port-forward is not for production. It's a developer/debugging tool. In production, use Ingress or LoadBalancer.


Diagnosing a Broken Service

Step 1 — Does the service exist?

kubectl get svc nginx-service

Step 2 — Does it have endpoints (pods)?

kubectl get endpoints nginx-service
# If <none> → selector doesn't match any pods

Step 3 — What port is the pod actually on?

kubectl describe pod nginx-pod | grep -i port

Step 4 — Does the service port match?

kubectl describe svc nginx-service
# Check: Port, TargetPort, Endpoints

Step 5 — Test connectivity from inside the cluster:

kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://nginx-service:80


Quick Reference

# Check pod port first
kubectl describe pod <pod> | grep Port

# Expose
kubectl expose pod <pod> --port=80 --name=<svc>
kubectl expose pod <pod> --port=80 --target-port=8080 --name=<svc>
kubectl expose deployment <dep> --port=80 --name=<svc>

# Port-forward
kubectl port-forward service/<svc> <local-port>:<svc-port>
kubectl port-forward service/<svc> 9090:80 &     # background
curl localhost:9090                               # test

# Diagnose
kubectl get endpoints <svc>                      # should show pod IPs, not <none>
kubectl describe svc <svc>                       # check Port + TargetPort