In a Kubernetes Service manifest, what is the difference between the port, targetPort and nodePort fields, and what does it mean when targetPort is a name instead of a number?
answer
- port = on the Service VIP; targetPort = on the pod
- targetPort defaults to port when omitted
- nodePort = on every node, 30000-32767
- named targetPort -> containerPort name, resolved per pod
- containerPort is documentation; binding to 127.0.0.1 breaks it
basics
~20 sport is the port the Service itself listens on (on its virtual IP). targetPort is the port on the backing pods that traffic is sent to. nodePort is the port opened on every node for NodePort/LoadBalancer Services. A named targetPort resolves to a containerPort with that name.
solid answer
~60 sThree different sides of the same hop: - **`port`** is the port clients use on the Service's virtual IP / DNS name, e.g. `http://api:80`. - **`targetPort`** is the port on the **pod** that traffic is rewritten to. It defaults to the same value as `port` if omitted, which is why so many manifests appear to only have one port. - **`nodePort`** is the port opened on **every node** and only applies to `NodePort` and `LoadBalancer` Services. Kubernetes allocates one from 30000-32767 unless you pin it. So `port: 80, targetPort: 8080` means "clients call port 80, the app listens on 8080". `targetPort` may be a **string**, in which case it refers to a `name` on a `containerPort` in the pod spec. That decouples the Service from the numeric port and lets different pods behind the same Service expose the port on different numbers, which is handy during a port migration. One trap: a pod's `containerPort` declaration is informational. Traffic reaches whatever port the process actually listens on, so a mismatch between `containerPort` and reality does not break routing, but a mismatch in `targetPort` does.
code
yaml · 30 linesapiVersion: v1
kind: Service
metadata:
name: api
spec:
type: NodePort
selector:
app: api
ports:
- name: http
port: 80
targetPort: http
nodePort: 30080
- name: metrics
port: 9090
targetPort: 9090
---
apiVersion: v1
kind: Pod
metadata:
name: api-1
labels:
app: api
spec:
containers:
- name: app
image: example/api:1.4
ports:
- name: http
containerPort: 8080go deeper
Say which side of the hop each field describes and that targetPort defaults to port. That alone answers the screening version.
Add named target ports and their per-pod resolution, multiple named ports, and the fact that containerPort is informational.
Use it diagnostically: read the endpoint list to see what actually resolved, distinguish a wrong targetPort from an app bound to loopback, and use named ports to make port migrations rollout-safe.
Treat port naming as a platform convention: consistent names (http, grpc, metrics) let ingress, mesh and scraping config be generated rather than hand-written per service.
## The three ports name three different endpoints When a client inside the cluster calls a Service, one packet gets rewritten once on its way to a pod. Each field names a different end of that rewrite. **`spec.ports[].port`** is the Service's own port: the port exposed on its virtual IP and, by extension, the port implied by its DNS name. `http://api.default.svc.cluster.local:80` uses `port: 80`. It is the only one of the three that clients care about. **`spec.ports[].targetPort`** is the destination port on the selected pods. The node dataplane rewrites the destination address to `<podIP>:<targetPort>`. If you omit `targetPort`, it defaults to the same numeric value as `port` - the source of the common belief that a Service has "one port". **`spec.ports[].nodePort`** exists only for `type: NodePort` and `type: LoadBalancer`. It is the port bound on every node so that outside traffic can enter. If you do not set it, Kubernetes allocates from the configured range (30000-32767 by default). Setting it by hand is allowed but invites collisions; the API server rejects a manifest whose pinned value is already taken. ## Named target ports `targetPort` accepts either an integer or a string. A string is looked up against the `name` field of a `containerPort` entry in each backing pod: Pod: `ports: [{name: http, containerPort: 8080}]`, Service: `targetPort: http`. The resulting endpoint is `podIP:8080`. Why bother? Because the mapping is resolved **per pod**. If you are migrating an app from 8080 to 9090, pods running the new image can declare `name: http, containerPort: 9090` while old pods still say 8080, and one unchanged Service routes correctly to both during the rollout. Port names are limited to 15 characters and must be IANA_SVC_NAME-shaped (lowercase alphanumeric and dashes, containing at least one letter). The catch: if a backing pod has no `containerPort` with that name, that pod produces no endpoint for the port. The Service then silently drops it from the pool, which looks like an intermittent "some requests fail" problem. ## containerPort is documentation, targetPort is not A frequent confusion: declaring `containerPort: 8080` in a pod spec does **not** open, publish or restrict anything. Container networking gives the pod a real IP, and any port the process binds is reachable from the cluster network regardless of what the manifest says. `containerPort` matters in exactly two places: as the target of a **named** `targetPort`, and as human/tooling documentation (some dashboards and `kubectl` heuristics read it). So a numeric `targetPort: 8080` works even if no `containerPort` is declared at all, as long as the process really listens on 8080. Conversely, declaring `containerPort: 8080` while the process listens on 3000 breaks nothing by itself, but any Service pointing at 8080 will fail to connect. ## Multiple ports `spec.ports` is a list, and once there is more than one entry each **must** have a `name` (single-port Services may omit it). Names also flow into the endpoint objects and are what tooling uses to disambiguate, e.g. a Service exposing `http` on 80 -> 8080 and `metrics` on 9090 -> 9090. `protocol` defaults to `TCP`; `UDP` and `SCTP` are the alternatives. A Service exposing both TCP and UDP on the same number needs two entries with distinct names. ## `appProtocol` `spec.ports[].appProtocol` is a hint (`http`, `https`, `grpc`, or a domain-prefixed custom value) that Kubernetes itself does not act on; ingress and mesh implementations read it to decide protocol handling. It is metadata, not behaviour. ## How this breaks in practice The classic failure is `port: 8080, targetPort: 80` written by autocomplete when the app actually listens on 8080. DNS resolves, the connection to the VIP is accepted by the dataplane, and then it hangs or is refused because nothing listens on 80 in the pod. Diagnosis is quick: `kubectl get endpointslices -l kubernetes.io/service-name=<svc>` shows the resolved `podIP:port` pairs, and `kubectl exec` into another pod plus `curl podIP:realPort` proves what the app truly listens on. If the endpoint list shows the wrong port number, the Service is misconfigured; if it shows the right one and the connection still fails, the app is probably bound to `127.0.0.1` rather than `0.0.0.0`.
- Does declaring containerPort in a pod spec open or expose that port?No. The pod already has its own IP on the cluster network, and any port the process binds is reachable; containerPort neither opens nor firewalls anything. It matters only as the target that a named targetPort resolves against, and as documentation for tools. A Service with a numeric targetPort works fine even when no containerPort is declared.
- The Service resolves and the endpoint list shows the correct podIP and port, but connections are still refused. What is the most likely cause?The application is bound to 127.0.0.1 inside the container instead of 0.0.0.0. Loopback is not reachable from outside the container's network namespace, so the dataplane delivers the packet and the kernel refuses it. Confirm by exec-ing into the pod and running a local curl (which succeeds) versus curling the pod IP from another pod (which fails), then change the app's bind address.
saying these in an interview costs you the question
- Saying targetPort is the port clients connect to.
- Thinking containerPort publishes or firewalls a port.
- Assuming nodePort applies to plain ClusterIP Services.
- Believing a named targetPort must match the Service port name.
- Forgetting that omitting targetPort makes it equal to port, then wondering why traffic hits the wrong port.