Skip to content

NodePort Service — Full Reference

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

See also: nslookup.md.md for ClusterIP, kubectl expose, CoreDNS, and DNS verification.


Exam Priority — Quickest Path

kubectl expose deployment my-app --name=my-nodeport-svc --port=80 --type=NodePort

That's it for basic NodePort creation. If you need a specific nodePort number, you need YAML:

kubectl expose deployment my-app --name=my-svc --port=80 --type=NodePort --dry-run=client -o yaml > svc.yaml
# edit nodePort value in svc.yaml
kubectl apply -f svc.yaml

What Is NodePort

NodePort is a Service type that exposes your application on a static port on every node in the cluster. Traffic hitting <any-node-IP>:<nodePort> gets forwarded to the Service, which load balances to the backing pods.

External traffic
<node-ip>:30080        ← NodePort (opens on every node)
Service ClusterIP:80   ← Service routes internally
Pod:8080               ← targetPort (container port)

Three ports involved:

Port Where it lives What it is
nodePort On every node's host network The externally accessible port (30000–32767 range)
port On the Service's ClusterIP The port the Service listens on internally
targetPort On the container The port the app actually runs on

ClusterIP vs NodePort vs LoadBalancer

Type Accessible from How
ClusterIP Inside cluster only Stable internal IP + DNS
NodePort Outside cluster <node-ip>:<nodePort> — any node, same port
LoadBalancer Outside cluster Cloud provider creates an external load balancer

NodePort pro: works without a cloud provider — useful for on-prem, bare metal, or testing.

NodePort con: exposes a port on every node (even nodes not running the pod). Port range is limited to 30000–32767. Not suitable for production (use LoadBalancer or Ingress instead).


NodePort YAML — Full Structure

apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-svc
  namespace: default
spec:
  type: NodePort
  selector:
    app: my-app              # routes to pods with this label
  ports:
  - port: 80                 # Service's internal port (ClusterIP side)
    targetPort: 8080         # container port the app listens on
    nodePort: 30080          # external port on every node (30000-32767)
                             # omit to let Kubernetes auto-assign
    protocol: TCP

If you omit nodePort, Kubernetes assigns a random port in the 30000–32767 range. For the exam, check what port was assigned:

kubectl get svc my-nodeport-svc -o jsonpath='{.spec.ports[0].nodePort}'


Creating NodePort — All Methods

Imperative (no control over nodePort number)

# From a deployment
kubectl expose deployment my-app \
  --name=my-nodeport-svc \
  --port=80 \
  --target-port=8080 \
  --type=NodePort

# From a pod
kubectl expose pod my-pod \
  --name=my-nodeport-svc \
  --port=80 \
  --type=NodePort

YAML (full control, needed for specific nodePort)

kubectl expose deployment my-app \
  --name=my-nodeport-svc \
  --port=80 \
  --type=NodePort \
  --dry-run=client -o yaml > svc.yaml

# then edit nodePort value in svc.yaml
kubectl apply -f svc.yaml

Testing NodePort Access

From inside the cluster (same as ClusterIP):

kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://my-nodeport-svc:80

From outside the cluster (using node IP):

# Get node IP
kubectl get nodes -o wide    # INTERNAL-IP column

# Hit the nodePort
curl http://<node-ip>:30080


Inspect a NodePort Service

kubectl get svc my-nodeport-svc
kubectl describe svc my-nodeport-svc     # shows selector, endpoints, NodePort value
kubectl get endpoints my-nodeport-svc    # shows pod IPs — if <none>, selector is wrong

# Get just the nodePort number
kubectl get svc my-nodeport-svc -o jsonpath='{.spec.ports[0].nodePort}'

# Get the ClusterIP
kubectl get svc my-nodeport-svc -o jsonpath='{.spec.clusterIP}'

Common Exam Patterns

"Expose a deployment externally on port 30080":

kubectl expose deployment my-app --name=my-svc --port=80 --type=NodePort --dry-run=client -o yaml > svc.yaml
# edit: add nodePort: 30080 under ports
kubectl apply -f svc.yaml

"What port is the service exposed on externally?":

kubectl get svc my-svc -o jsonpath='{.spec.ports[0].nodePort}'

"Verify the service routes to the correct pods":

kubectl get endpoints my-svc    # shows pod IPs
kubectl get pods -o wide        # compare with pod IPs to confirm match

Debugging no traffic:

kubectl get endpoints my-svc         # if <none> — selector doesn't match any pods
kubectl describe svc my-svc          # check Selector field
kubectl get pods --show-labels       # check pod labels match the selector


Quick Reference

# Create
kubectl expose deployment NAME --name=SVC --port=80 --type=NodePort
kubectl expose deployment NAME --name=SVC --port=80 --target-port=8080 --type=NodePort

# Inspect
kubectl get svc
kubectl describe svc NAME
kubectl get endpoints NAME
kubectl get svc NAME -o jsonpath='{.spec.ports[0].nodePort}'

# Test
curl http://<node-ip>:<nodePort>
kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://SVC-NAME:PORT