1. Ingress Resource vs. Ingress Controller
This distinction trips almost everyone up the first time: an Ingress resource, by itself, does nothing. It's a routing declaration — rules saying "traffic for this hostname/path should go to that Service." An Ingress controller is a separate piece of software, running as Pods in your cluster, that actually reads Ingress resources and does the routing.
Ingress resource # a YAML object declaring routing RULES. Inert on its own —
# creating one with no controller installed does nothing.
Ingress controller # actual running software (commonly NGINX Ingress, but
# Traefik and cloud-specific ones exist too) that watches
# for Ingress resources and configures a real reverse
# proxy/load balancer to match.
This mirrors a pattern you've now seen repeatedly: a declarative Kubernetes object paired with a controller that reconciles reality toward it — the same shape as Deployments and ReplicaSets, applied here to routing instead of Pod count.
EKS, GKE and AKS typically expect you to install an Ingress controller yourself (or use their cloud-specific one). If your Ingress resources aren't doing anything, checking "is a controller actually installed and running?" should be your very first debugging step.
2. Host- & Path-based Routing
One Ingress can route many different hostnames and paths to different backend Services — this is the actual mechanism that lets one public IP serve an entire cluster's worth of apps.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api
port:
number: 80
app.example.com and api.example.com can share the exact
same public IP and load balancer — the Ingress controller inspects the incoming
request's Host header (and path) and routes accordingly, all before
the request ever reaches a Service.
You almost never expose frontend or api as LoadBalancer Services themselves once Ingress is in front of them — internal ClusterIP is enough, since the Ingress controller is the single public entry point routing to them.
3. Installing NGINX Ingress
Getting a controller running on your local cluster:
# Minikube ships an addon:
minikube addons enable ingress
# kind / other clusters — install via the official manifest:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
kubectl get pods -n ingress-nginx # confirm the controller Pods are Running
kubectl get ingress # your Ingress resources now show an assigned ADDRESS
Once installed, apply your ingress.yaml from Section 2 and, for local
testing without real DNS, map hostnames to your cluster's IP in your machine's
hosts file:
# /etc/hosts (or C:\Windows\System32\drivers\etc\hosts on Windows)
127.0.0.1 app.example.com
127.0.0.1 api.example.com
# For Minikube specifically, you may need: minikube tunnel
curl http://app.example.com
On a real cloud cluster, you'd point an actual DNS A record (app.example.com) at the LoadBalancer IP the Ingress controller itself is exposed through — the hosts-file trick above is purely a local-development stand-in for that.
4. TLS with cert-manager
Terminating HTTPS at the Ingress means your backend Services never have to handle TLS themselves — one certificate, in one place, for the whole cluster's public traffic. cert-manager automates obtaining and renewing those certificates, commonly from Let's Encrypt, for free.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls-cert # cert-manager populates this Secret automatically
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
Note that a TLS certificate ends up stored as — unsurprisingly — a Kubernetes Secret, the exact object type from Week 10. cert-manager handles the entire domain-validation and renewal process automatically once configured, which is the difference between a certificate that quietly expires one day and one that never does.
Let's Encrypt validates domain ownership over the public internet — it can't issue a certificate for a local-only hostname like app.example.com in your hosts file. This piece is worth understanding conceptually now and trying hands-on once you have a real domain and a cloud cluster, in Week 18's capstone.
5. Hands-on Exercise
Route two apps through one Ingress, by hostname and by path
Install a real Ingress controller locally, then expose two different backend Services through a single entry point using both routing styles.
Part 1 — Install and route by path:
- Install the NGINX Ingress controller on your local cluster, and confirm its Pods are Running.
- Deploy two simple apps (reuse images from earlier weeks, or any two containers that each respond distinctly, e.g. two different
httpdcontainers each serving different static text) as separate Deployments + ClusterIP Services. - Write an Ingress routing
/app1to the first Service and/app2to the second, both on the same host. - Confirm both paths route correctly to the right backend.
If path-based routing serves the wrong app's static assets (a common symptom: HTML loads but CSS/JS 404), it's usually because the app itself doesn't know it's being served from a subpath — the rewrite-target annotation from Section 2 is often necessary to strip the prefix before it reaches the backend.
Part 2 — Add host-based routing:
- Add two entries to your hosts file mapping two fake hostnames to your cluster's local IP (127.0.0.1 for most local setups).
- Rewrite your Ingress to route by hostname instead of path — one Service per hostname, both back to root path
/. - Confirm each hostname correctly reaches its own app, and that a request to a hostname NOT listed in your Ingress rules gets a 404 from the Ingress controller itself (not from either backend).
- Run
kubectl describe ingress main-ingressand identify exactly which section of the output shows the host-to-backend mapping you configured.
A 404 for an unrecognized hostname coming from the Ingress controller itself (check the response headers for "nginx") — rather than from either of your apps — is proof the routing decision is happening at the Ingress layer, before your Services are ever involved.
6. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
You applied an Ingress resource but requests aren't being routed at all. What should you check first?
You applied an Ingress resource but requests aren't being routed at all. What should you check first?
Whether an Ingress controller is actually installed and running in the cluster. An Ingress resource by itself is only a routing declaration — inert without a controller (like NGINX Ingress) actively watching for it and configuring a real proxy to match.
Q2
Why can two completely different apps share the same public IP and load balancer through Ingress?
Why can two completely different apps share the same public IP and load balancer through Ingress?
The Ingress controller inspects the incoming request's Host header and/or path before deciding which backend Service to route to — this lets one entry point (and one cloud load balancer) multiplex traffic for many different hostnames and paths to many different backend apps.
Q3
Once Ingress is in front of your apps, what Service type do the backend Services typically need?
Once Ingress is in front of your apps, what Service type do the backend Services typically need?
ClusterIP. The Ingress controller is the single public entry point, so backend Services generally only need to be reachable from inside the cluster — exposing each of them individually as LoadBalancer Services would be redundant and defeat the purpose of consolidating through Ingress.
Q4
Where does a TLS certificate obtained by cert-manager actually get stored?
Where does a TLS certificate obtained by cert-manager actually get stored?
As a Kubernetes Secret — the same object type covered in Week 10 — referenced by the Ingress's tls.secretName field. cert-manager automates populating and renewing that Secret's contents from a certificate authority like Let's Encrypt.