Let’s be honest: you just want your container to talk to the internet.
Instead, someone handed you a YAML file with three different kind: fields and said “It depends.”
If you’re a backend or full-stack dev who breaks out in hives at the thought of kubectl get nodes, this is for you.
We’re going to ignore the networking theory. No VxLAN, no iptables deep dives, no BGP. Instead, let’s map Kubernetes networking onto concepts you already understand: function calls, localhost, API gateways, and that one time you tried to host a game server from your dorm room.
The mental model (skip if you just want the cheat sheet)
Think of your cluster like a apartment building.
Pod = a person inside an apartment. Has a private address (10.0.0.5). No doorbell. No mail slot. You cannot reach them from outside the building. They don’t even know the outside exists.
Service = the building’s mailroom. It gives a stable internal address so other residents can send mail to “Apartment 3B” even if the person moves to 4C. Still internal only.
LoadBalancer = your front door, with a public street address. Anyone on the internet can knock. But it only points to one apartment at a time.
Ingress = the building’s receptionist / HTTP router. You say “I need to speak to /api/payments”, she looks at the sign, and sends you to Apartment 3B. You say “/dashboard”, she sends you to Apartment 7A. One public door, many destinations.
Now let’s translate that to actual YAML (the parts you care about).
- Service (ClusterIP) – “localhost for the cluster” You need this when: Your pods need to talk to each other.
A Service of type ClusterIP (the default) gives you one stable DNS name inside the cluster. Pods come and go. The Service doesn’t care.
yaml
apiVersion: v1
kind: Service
metadata:
name: payments-api
spec:
selector:
app: payments # routes to any pod with this label
ports:
- port: 8080 # what the service listens on
targetPort: 3000 # what your container actually uses
From any other pod: curl http://payments-api:8080/health — works every time.
What it can’t do: be reached from your laptop. Or the internet. Or your mom’s iPad.
If you only have this, you’re still in private dev mode.
- LoadBalancer – “the brute force approach” You need this when: You have exactly one service that needs public access, and you don’t care about cost (cloud LB starts at ~$20/mo).
yaml
apiVersion: v1
kind: Service
metadata:
name: payments-api-public
spec:
type: LoadBalancer # <-- the magic line
selector:
app: payments
ports:
- port: 80
targetPort: 3000
Run kubectl get svc → EXTERNAL-IP will show (after a minute) 203.0.113.42.
Hit that IP from anywhere. Done.
The catch: one public IP per service. If you have three microservices, you pay for three LBs. Also, no path‑based routing. No host‑based routing. No SSL termination (unless you bolt on annotations).
It’s like renting a dedicated front door for every single room in your apartment.
- Ingress – “one door to rule them all” You need this when: You have multiple HTTP services (web, API, admin dashboard, docs) and you want one single IP address + domain.
An Ingress is not a Service type. It’s a separate object that needs an Ingress Controller (nginx, Traefik, AWS ALB, etc.) running in your cluster.
First, install an Ingress Controller (one-time ops task, sorry). Then:
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-router
spec:
rules:
- host: api.myapp.com
http:
paths:
- path: /payments pathType: Prefix backend: service: name: payments-api # points to a regular ClusterIP Service port: number: 8080
- host: myapp.com
http:
paths:
- path: / pathType: Prefix backend: service: name: frontend-web port: number: 80 Now:
myapp.com/dashboard → frontend-web
api.myapp.com/payments → payments-api
SSL? Add a tls: section. Path rewriting? Annotations. Rate limiting? Ingress controller config.
One public endpoint. Unlimited backend services.
The cheat sheet (tape this to your monitor)
Want to… Use YAML snippet
Let Pod A talk to Pod B Service (ClusterIP) type: ClusterIP (default)
Expose a TCP/UDP service to the internet (e.g., Redis, a game server, raw TCP) LoadBalancer type: LoadBalancer
Expose multiple HTTP services on one IP with path/host routing Ingress + a Service (ClusterIP) kind: Ingress
Reach a service from your laptop for debugging (no cloud LB) kubectl port-forward kubectl port-forward svc/payments-api 8080:8080
Get a static IP but still want HTTP routing (cloud-specific) LoadBalancer + annotations (e.g., AWS NLB + Ingress) Read your cloud’s docs. Sorry.
The “wait, but why do I need all three?” example
You write a checkout service:
You create a ClusterIP Service so your orders pod can call checkout:8080/create.
Your PM says “expose it for a test”. You add type: LoadBalancer — it works, but now you have an IP per service and no SSL.
You add a second service (coupons). You now have two public IPs. Your DNS looks like a crime scene.
You finally install an Ingress Controller. You change both services back to ClusterIP (they were always internal). You write one Ingress rule: /api/* → checkout, /coupons/* → coupons.
You go from 2 LBs ($40/mo) + manual SSL to 0 LBs + automatic certs. Your ops person buys you coffee.
The one sentence summary
Service = internal DNS + stable IP for pod-to-pod.
LoadBalancer = one public IP per service (expensive, simple).
Ingress = one public entry point for many HTTP services (cheap, powerful).
Now go back to writing actual code. You’re welcome.
Top comments (0)