Ingress Controllers
Summary
- An ingress controller is the running component that watches
Ingressobjects and programs a real proxy or cloud load balancer to route traffic. - Insight: The
IngressAPI is inert. The controller is what turns YAML into actual HTTP routing. - Key Concepts: IngressClass, controller data plane, ingress-nginx, AWS Load Balancer Controller, Traefik, leader election.
- When to Use: Install one whenever you need host or path based HTTP routing, TLS termination, or a stable public entry point for cluster workloads.
- Limitations: Annotation features are controller-specific and not portable. Running multiple controllers requires careful IngressClass scoping.
- Related: Ingress objects, TLS certificates, and the Gateway API as the successor routing model.
Recipe
Install a controller, confirm its IngressClass, then reference that class from your Ingress objects.
Most teams run exactly one general-purpose controller per cluster and scope any others by class.
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
--set controller.replicaCount=2
kubectl get ingressclassWorking Example
Deploy ingress-nginx with two replicas for availability, then verify the class and external address.
kubectl -n ingress-nginx get deploy,svcThe controller's own Service is usually type: LoadBalancer, which provisions a cloud load balancer as the public entry point.
An Ingress then binds to the controller by class:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80Confirm the controller claimed it and assigned an address:
kubectl describe ingress webThe events should show the controller syncing, and ADDRESS should populate once the load balancer is ready.
Deep Dive
What the controller actually does
The controller runs a control loop. It watches Ingress, Service, EndpointSlice, and Secret objects, computes a proxy configuration, and reloads or hot-updates its data plane.
For ingress-nginx the data plane is nginx. For Traefik it is Traefik's own proxy. For the AWS Load Balancer Controller it is an ALB provisioned in your account.
IngressClass and binding
IngressClass is the link between an Ingress object and a controller.
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginxAn object with ingressClassName: nginx is served only by the controller whose spec.controller matches. The default-class annotation lets objects that omit the field be claimed automatically.
The "one per cluster" rule of thumb
Two controllers watching the same IngressClass will both try to serve those objects, causing conflicts and duplicate load balancers.
The common pattern is one general controller for most traffic, plus optionally a second controller scoped to its own class (for example an internal-only class for private LBs).
Scope cleanly: distinct IngressClass names, and if needed the --ingress-class or watch-namespace flags so each controller only reconciles its own objects.
Choosing a controller
ingress-nginx is the community default, portable across clouds, rich annotation set, runs as pods.
AWS Load Balancer Controller provisions ALBs directly, so L7 routing happens in AWS rather than in-cluster pods; ideal on EKS.
Traefik offers a strong Kubernetes-native CRD model and good Gateway API support.
Envoy-based controllers (Contour, Emissary) give advanced L7 features and are natural on-ramps to Gateway API.
High availability
Run at least two controller replicas with a PodDisruptionBudget and spread constraints.
The proxy replicas serve traffic in parallel; leader election (where used) only coordinates control-plane bookkeeping, not the data path.
Gotchas
No controller installed. Applying an Ingress with no controller present does nothing and produces no error. Always confirm a controller and matching IngressClass exist.
Wrong or missing ingressClassName. If the name does not match an installed class, no controller claims the object. With no default class set, omitting the field also leaves it unclaimed.
Two controllers fighting. Two controllers sharing a class create duplicate cloud load balancers and inconsistent routing. Give each its own class.
Annotation drift. Copying annotations between controllers rarely works, since keys like nginx.ingress.kubernetes.io/* are ignored by Traefik or ALB. Re-map features per controller.
Reload cost under churn. Some controllers reload the proxy on config changes. Heavy Ingress churn can cause reload storms; prefer controllers with dynamic reconfiguration for high-change environments.
Assuming Docker runs the proxy. The controller pods run on nodes under containerd via the CRI, not Docker Engine. Docker is only for building the images.
Alternatives
Gateway API with a Gateway controller. The successor to Ingress, with a role-oriented, more expressive API. Prefer it for new advanced routing; many controllers support both.
Service type LoadBalancer only. Pure L4 exposure without host or path routing or TLS SNI. Simpler, but no HTTP intelligence.
Service mesh gateway. Istio or Linkerd gateways handle north-south traffic alongside mesh features. Choose when you already run a mesh.
API gateway product. A dedicated gateway (Kong, cloud API gateway) when you need auth, rate limiting, and developer portals beyond routing.
FAQs
Can I run more than one ingress controller? Yes, if each watches a distinct IngressClass (or namespace). A common split is public and internal-only controllers.
Does every Ingress need ingressClassName? Set it explicitly for clarity. You can rely on a default IngressClass, but explicit binding avoids surprises.
Why is my Ingress ADDRESS empty? The controller has not reconciled it, the class does not match, or the cloud load balancer is still provisioning.
Is ingress-nginx the same as nginx Inc's controller? No. ingress-nginx is the Kubernetes community controller. F5/NGINX ships a separate product. They differ in annotations and features.
Does the controller terminate TLS? Typically yes. It holds the certificate and decrypts requests, then forwards to pods. See the TLS certificates page.
Related
- How External Traffic Reaches Your Pods
- Ingress Basics
- TLS Certificates
- Gateway API
- Ingress Best Practices
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).