Ingress & Ingress Controller
Learn how Ingress and Ingress Controllers route external traffic to Kubernetes services. This lesson covers the core concepts, compares options, provides a hands-on walkthrough, and prepares you for next steps.
Focus: understand ingress and ingress controller
Imagine you have a dozen microservices running inside your Kubernetes cluster, each with its own ClusterIP Service. You want the whole world to reach them on yourapp.com/api and yourapp.com/dashboard — but the only way in so far is a clunky NodePort or a per-service LoadBalancer. Every new service burns a new cloud load balancer (cha-ching 💸) and you have to manually juggle hostnames and TLS certificates. There’s a smarter, unified way: Kubernetes Ingress combined with an Ingress Controller. This lesson will replace your port-forwarding cobwebs with a clean, scalable external routing layer.
The problem this lesson solves
Without Ingress, exposing services to the outside world is messy:
NodePortuses high ephemeral ports like30080— ugly for users.LoadBalancerper service creates one cloud load balancer each (costly, slow).- No built-in support for host‑based routing (
api.example.comvsapp.example.com). - TLS termination must be handled per Service or offloaded to a reverse proxy.
An Ingress resource alone does nothing — it’s just a declarative wish. The real magic happens when an Ingress Controller (a reverse-proxy pod) reads that wish and configures the underlying proxy (like Nginx, Traefik, or HAProxy). The problem: new learners often confuse the resource with the controller and then wonder why nothing works.
Core concept / mental model
Think of Ingress as a router configuration file and the Ingress Controller as the router daemon that makes it real.
| Layer | Analogy | Kubernetes component |
|---|---|---|
| Ingress (resource) | A floor plan for the router | YAML with rules, paths, TLS |
| Ingress Controller | The router itself | A Pod running Nginx/Traefik/HAProxy |
| Service | A door to an apartment | ClusterIP service pointing to Pods |
Key definitions
- Ingress resource: A Kubernetes API object (
apiVersion: networking.k8s.io/v1,kind: Ingress) that defines rules for routing external HTTP/S traffic to Services based on hostnames and paths. - Ingress Controller: A running process (Pod) that watches the Kubernetes API for Ingress resources and configures its backend (e.g., Nginx) accordingly. Common controllers:
ingress-nginx,traefik,haproxy-ingress,contour.
💡 Pro tip: If you create an Ingress resource without a controller installed, your Service stays unreachable from outside the cluster. The resource is just a declaration; the controller is the execution.
How it works step by step
- A user sends a request — e.g.,
curl https://api.example.com/users. - DNS resolves the hostname to the IP of whichever load balancer fronts the cluster (cloud LB or node port).
- The load balancer forwards traffic to an Ingress Controller Pod (often running as a
DeploymentwithhostNetworkor aServiceof typeLoadBalancer). - The Ingress Controller matches the request against its internal routing table (built from Ingress resources).
- The request is proxied to the correct Kubernetes Service (e.g.,
api-service:8080). - The Service load‑balances to one of its backing Pods.
Anatomy of an Ingress YAML
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx # selects which controller handles this
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: user-svc
port:
number: 8080
ingressClassName: ties this resource to a specific controller (requires--watch-ingress-without-classor you set the class).pathType:PrefixorExact— determines path matching.backend: whichServiceand port to route to.
Hands-on walkthrough
We’ll install the ingress-nginx controller (most common) and create an Ingress resource to route traffic to a simple hello-world Service.
Step 1: Install an Ingress Controller
Use Helm or plain manifests. Here’s with Helm:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx
Wait for the controller Pod to become ready:
kubectl get pods -n default -l app.kubernetes.io/name=ingress-nginx
# NAME READY STATUS RESTARTS AGE
# ingress-nginx-controller-7b8b6b7b9c-abcde 1/1 Running 0 2m
Check the Service type (it will be LoadBalancer if your cluster supports it):
kubectl get svc ingress-nginx-controller
If you’re using minikube, you can expose the controller with:
minikube service ingress-nginx-controller
Or tunnel to get a LoadBalancer IP:
minikube tunnel
Step 2: Deploy the backend Service and Pods
# hello.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: hello
name: hello-pod
spec:
containers:
- image: hashicorp/http-echo:latest
name: hello
args: ["-text", "Hello from k8s!"]
---
apiVersion: v1
kind: Service
metadata:
name: hello-svc
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 5678 # http-echo listens on 5678
Apply:
kubectl apply -f hello.yaml
Step 3: Create an Ingress resource
# ingress-hello.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
spec:
ingressClassName: nginx
rules:
- host: hello.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-svc
port:
number: 80
Apply:
kubectl apply -f ingress-hello.yaml
Step 4: Test the routing
Add a local /etc/hosts entry (or use the cluster’s external IP and a Host header):
curl -H "Host: hello.example.com" http://<EXTERNAL-IP>
# Hello from k8s!
💡 If you’re using
minikube, runminikube ipto get the IP, thenminikube tunnelin a separate terminal, and use the cluster IP fromkubectl get svc ingress-nginx-controller.
Compare options / when to choose what
| Controller | Strengths | Best for |
|---|---|---|
| ingress-nginx | Huge community, flexible, supports TCP/UDP, Lua plugins | General‑purpose, production |
| Traefik | Auto‑discovery, nice dashboard, Let’s Encrypt built‑in | Microservices heavy, edge routers |
| HAProxy Ingress | Battle‑tested, low latency, advanced ACLs | High‑traffic, security‑conscious |
| Contour | Envoy proxy, gRPC support, dynamic config | Envoy users, gRPC heavy |
| AWS ALB Ingress Controller | Native AWS ALB, supports WebSocket | EKS users with ALB integration |
When to choose what: - Use ingress-nginx for most workloads — it’s the default choice. - Use Traefik if you want automatic Let’s Encrypt and a management dashboard. - Use HAProxy if you need extreme throughput or have complex access rules. - Use cloud‑native controllers (AWS ALB, GCE) if you want to offload load balancing to the cloud provider.
Troubleshooting & edge cases
Ingress resource created but no route works
- Problem:
kubectl get ingressshows the resource, butcurlreturns502orconnection refused. - Check: Is the Ingress Controller running?
bash
kubectl get pods -n ingress-nginx
- Check: Does the controller’s Service have an external IP? If it’s
pending, you may needmetallborminikube tunnel.
Wrong pathType causing 404
Exactmatches only the literal path (e.g.,/foowon’t match/foo/bar).Prefixmatches any subpath (e.g.,/foo/barmatches/foo).
curl -H "Host: hello.example.com" http://<IP>/something
# 404 if pathType: Exact and path: /
Host header mismatch
- If your Ingress rule has
host: hello.example.combut you don’t send that Host header, the controller doesn’t match the rule. - Fix:
curl -H "Host: hello.example.com"or use--resolveflags.
Multiple Ingress Controllers in the same cluster
- You must use
ingressClassNameand each controller must be configured with a different--ingress-classflag (e.g.,nginxvstraefik). - Omission of
ingressClassNamemay default to the cluster’s default controller (if one is set).
TLS certificate not working
- Certificates must be stored in a
Secretin the same namespace as the Ingress resource.
spec:
tls:
- hosts:
- hello.example.com
secretName: hello-tls
What you learned & what's next
You now understand: - The difference between an Ingress resource and an Ingress Controller. - How to install an Ingress Controller (ingress-nginx via Helm). - How to create an Ingress resource with host‑based routing. - How to test the route end‑to‑end. - How to compare controller options (nginx, Traefik, HAProxy). - How to troubleshoot common issues (no controller, path matching, host headers).
Next lesson: Secure your Ingress with TLS/HTTPS — learn how to manage certificates using cert-manager.
Practice recap
Install the ingress-nginx controller (or another of your choice) into your cluster. Deploy a simple echo Pod and Service, then create an Ingress resource with a rule for echo.local and path /*. Add the IP and Host header to your /etc/hosts and test with curl. Once that works, try adding a second rule with a different hostname and path.
Common mistakes
- Creating an Ingress resource without installing an Ingress Controller — the resource will be accepted but no traffic will be routed.
- Using
pathType: Exactexpecting subpaths to match —/only matches the root; usePrefixfor wildcard matching. - Forgetting to set the
Hostheader in curl when testing local Ingress — without it the rule won’t match. - Leaving
ingressClassNameempty when multiple controllers exist — the wrong controller may pick it up or none at all.
Variations
- Use
Traefikas a lightweight alternative — it supports automatic Let's Encrypt and a built-in dashboard. - For cloud‑native environments, use the
AWS Load Balancer ControllerorGKE Ingressto offload to managed LBs. - Run the Ingress Controller on
hostNetworkmode instead of behind aLoadBalancerService for bare‑metal setups.
Real-world use cases
- Route
api.example.comto an API Service andapp.example.comto a frontend Service using the same Ingress. - Expose multiple microservices like
orders,payments, andinventoryon different paths under a single domain. - Terminate TLS at the Ingress Controller and forward unencrypted traffic to backend Services inside the cluster.
Key takeaways
- Ingress is a routing resource; the Ingress Controller is the actual proxy that implements the rules.
- Without an Ingress Controller, an Ingress resource does nothing — install one before creating rules.
- Use
ingressClassNameto bind rules to a specific controller, especially in multi‑controller clusters. - Path types (
PrefixvsExact) determine how URL paths are matched — choose wisely. - Always verify routing with explicit
Hostheaders during development to avoid mismatches.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.