skip to content

Service Types

ClusterIP, NodePort, LoadBalancer, ExternalName and headless Services each expose the same pods differently, with a label selector mapping to EndpointSlices. Interviewers give a scenario and expect the right type plus port versus targetPort.

part ofKubernetesoverview, primer and where to startread it →
on this pageshow

questions

6

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?

level: juniorimportance: must knowfreq 68%

answer

  1. port = on the Service VIP; targetPort = on the pod
  2. targetPort defaults to port when omitted
  3. nodePort = on every node, 30000-32767
  4. named targetPort -> containerPort name, resolved per pod
  5. containerPort is documentation; binding to 127.0.0.1 breaks it

basics

~20 s

port 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 s

Three 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 lines
yaml
apiVersion: 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: 8080

go deeper

for a junior

Say which side of the hop each field describes and that targetPort defaults to port. That alone answers the screening version.

for a middle

Add named target ports and their per-pod resolution, multiple named ports, and the fact that containerPort is informational.

for a senior

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.

for a principal

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.

context

open as a page

A Kubernetes Service manifest can set spec.type to ClusterIP, NodePort, LoadBalancer or ExternalName. What does each type give you, and how do they relate to one another?

level: juniorimportance: must knowfreq 78%

basics

~20 s

ClusterIP gives a stable cluster-internal virtual IP (the default). NodePort adds a fixed port on every node. LoadBalancer adds an external cloud load balancer. ExternalName is only a DNS CNAME to an external host: no virtual IP, no proxying.

open as a page

A Kubernetes Service exists and its cluster DNS name resolves, but every connection to it fails and the Service has no backend addresses at all. How does a Service decide which pods back it, and how would you work out why the backend list is empty?

level: middleimportance: must knowfreq 62%

basics

~20 s

A controller watches pods matching the Service's label selector in the same namespace and publishes the ready ones as the Service's backend addresses. An empty list means no pod matches the selector, matching pods are not Ready, or the pods are in another namespace.

open as a page

What does setting clusterIP: None on a Kubernetes Service do, and when would you choose that over a Service with a normal virtual IP?

level: middleimportance: should knowfreq 52%

basics

~20 s

It makes the Service headless: no virtual IP is allocated and no load balancing happens. Cluster DNS instead returns the individual pod IPs for the Service name, letting clients see and address each backing pod directly.

open as a page

A Kubernetes Service exposed outside the cluster has externalTrafficPolicy set to Cluster by default, and it can be set to Local instead. What changes when you switch it, and what are the tradeoffs?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Cluster forwards external traffic to a pod on any node, adding a hop and rewriting the source IP to the node's. Local only serves traffic from pods on the receiving node, preserving the real client IP but dropping traffic on pods-less nodes and risking uneven load.

open as a page

How do you make a Kubernetes Service send repeated requests from the same client to the same backing pod, and what are the limitations of that mechanism?

level: middleimportance: nice to knowfreq 34%

basics

~20 s

Set spec.sessionAffinity: ClientIP, optionally tuning sessionAffinityConfig.clientIP.timeoutSeconds (default 10800). It hashes the client source IP to a pod. It is L4 only, breaks behind NAT or a proxy, and gives no stickiness when the pod dies or the pod set changes.

open as a page