Kubernetes Ingress vs LoadBalancer vs NodePort Explained
Understand the three ways to expose a Kubernetes service to the outside world, the tradeoffs of each, and how to pick the right one for your workload.
What you'll learn
- ✓What ClusterIP, NodePort, and LoadBalancer actually do
- ✓How an Ingress controller fits on top of Services
- ✓When to pay for cloud LBs vs sharing one Ingress
- ✓TLS termination patterns
- ✓How to debug stuck external traffic
Prerequisites
- •Familiarity with kubectl and basic Kubernetes objects
What and Why
A Service in Kubernetes is a stable virtual IP and DNS name in front of a set of pods. By itself, a Service is only reachable inside the cluster. To accept traffic from outside, you choose one of three exposure strategies: NodePort, LoadBalancer, or Ingress. Each one solves a different problem, and mixing them up leads to expensive cloud bills or unreachable apps.
Mental Model
- ClusterIP (default): internal only. Pods reach the service by DNS.
- NodePort: opens the same high port (30000–32767) on every node. External clients hit any node IP on that port.
- LoadBalancer: provisions a cloud load balancer (ELB, NLB, GLB) that forwards to NodePorts. One external IP per service.
- Ingress: a single shared entry point that routes HTTP(S) traffic by host and path to many backend services. Implemented by an Ingress controller (NGINX, Traefik, ALB, Istio).
NodePort:
client -> nodeIP:31234 -> kube-proxy -> pod
LoadBalancer:
client -> cloud LB -> nodeIP:31234 -> kube-proxy -> pod
Ingress:
client -> cloud LB -> ingress-controller pods -> Service ClusterIP -> pod
(routes by Host/path) Hands-on Example
Start with a Deployment and a ClusterIP Service:
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
replicas: 2
selector: { matchLabels: { app: web } }
template:
metadata: { labels: { app: web } }
spec:
containers:
- name: web
image: nginx:1.27
ports: [{ containerPort: 80 }]
---
apiVersion: v1
kind: Service
metadata: { name: web }
spec:
selector: { app: web }
ports: [{ port: 80, targetPort: 80 }]
To expose via NodePort:
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 31080
Now curl http://<any-node-ip>:31080 reaches the pods. Cheap and simple, but you must know node IPs and open the high port in firewalls.
To expose via LoadBalancer (on a cloud cluster):
spec:
type: LoadBalancer
ports: [{ port: 80, targetPort: 80 }]
The cloud provisions a real load balancer and writes its external IP into status.loadBalancer.ingress. Each LoadBalancer service costs roughly the price of one ELB or NLB per month.
For many services, use a single Ingress instead:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts: [app.example.com]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port: { number: 80 }
- path: /api
pathType: Prefix
backend:
service:
name: api
port: { number: 80 }
One cloud LB now fronts the Ingress controller, which routes by host and path to many ClusterIP services.
Common Pitfalls
Using LoadBalancer per service. Five services equal five cloud LBs and five bills. Use an Ingress for HTTP traffic.
Ingress without a controller. Creating an Ingress object does nothing on its own. You must install a controller (ingress-nginx, traefik, AWS Load Balancer Controller) and reference it via ingressClassName.
externalTrafficPolicy confusion. With the default Cluster, source IPs are SNAT’d to the node IP. Set externalTrafficPolicy: Local to preserve client IPs, but be aware it skips nodes without local pods and breaks even balancing.
NodePort port collisions. Two services cannot share the same NodePort. Let Kubernetes assign one unless you really need a fixed value.
TLS at the wrong layer. If your LB terminates TLS, the Ingress controller sees plain HTTP. If both terminate, you double-encrypt. Pick one termination point and document it.
Practical Tips
For non-HTTP protocols (gRPC streaming, raw TCP, UDP), use Service type: LoadBalancer with NLB-style annotations, or a Gateway API implementation. Classic Ingress is HTTP-only.
Inspect what is actually exposed:
kubectl get svc -A
kubectl describe ingress web-ingress
kubectl get endpoints web
endpoints is the most useful debug command — if it is empty, your Service selector does not match any pods, and no amount of LoadBalancer money will fix it.
Use cert-manager for automatic Let’s Encrypt certificates. Annotate the Ingress and let it issue and renew silently. Pair with external-dns to auto-create Route 53 or Cloud DNS records.
For multi-tenant clusters, give each team its own Ingress class so tenants do not stomp on shared controller config.
Wrap-up
Pick NodePort for quick experiments and on-prem clusters without a cloud LB. Pick LoadBalancer when a single service truly needs its own dedicated external IP, often for non-HTTP traffic. Pick Ingress for everything HTTP, because one shared entry point with host and path routing is cheaper, simpler, and more powerful. Always pair Ingress with a real controller, mind your TLS termination point, and let kubectl get endpoints be your first stop when traffic disappears into the void.
Related articles
- Kubernetes Kubernetes Networking: Services, kube-proxy, and CNI Plugins
How traffic actually flows in a Kubernetes cluster: the pod network, services, kube-proxy modes, and what a CNI plugin really does.
- Kubernetes Kubernetes Network Policies: A Practical Tutorial
Learn how Kubernetes NetworkPolicies work, how to lock down pod-to-pod traffic with selectors, and how to roll out a default-deny posture without breaking your cluster.
- Kubernetes Kubernetes Ingress: Routing External Traffic
How Ingress, IngressClass, and Ingress controllers route external HTTP and HTTPS traffic to your Kubernetes Services — with host and path rules, TLS termination, and notes on ingress-nginx vs Traefik.
- DevOps Kubernetes Services Explained: ClusterIP, NodePort, LoadBalancer
A clear guide to Kubernetes Services. Learn what ClusterIP, NodePort, LoadBalancer, and headless Services do, and when to use each in real clusters.