Kubernetes Ingress: One Door, Many Rooms, And the Controller That Actually Opens It

April 21, 2026


A Service in Kubernetes is an internal address. It is reachable from inside the cluster and only from inside the cluster. The moment you want a browser to hit app.example.com and land on the right pod, you are no longer talking about Services. You are talking about Ingress.

Ingress is the HTTP and HTTPS router that lives at the cluster edge. It does three things that L4 Services cannot. It terminates TLS so your backends do not each need certificates. It routes by hostname and URL path, so app.example.com/api can hit one Service and app.example.com/auth can hit another. And it gives you a single public entry point that hides the internal Service topology from the outside world.

The mental model that trips people up: the Ingress resource is not a running thing. It is a piece of declarative config. Nothing happens until an Ingress Controller picks it up and turns the rules into a real proxy. The controller is the actual data path. It is usually nginx, Traefik, HAProxy, or a cloud-managed one like the AWS ALB Ingress Controller or GKE's built-in. The controller watches Ingress objects, generates its own native config, and reloads.

That separation buys flexibility. You can run multiple controllers in the same cluster. Maybe nginx for internal apps, ALB for public traffic. Each Ingress declares which controller should own it via the ingressClassName field. The controllers only act on Ingresses whose class matches their own.

Which leads to the failure I have watched eat half a day of debugging time. A team migrated from the older kubernetes.io/ingress.class annotation to the newer ingressClassName field. They updated half their manifests. The Ingresses that still used the annotation got picked up. The new ones did not match any installed IngressClass. No controller claimed them. The Ingress objects existed in the cluster, kubectl get ingress showed them fine, but the external load balancer never learned the routes. Every request returned 404 from the default backend. Nothing in the controller logs complained, because as far as nginx was concerned, those Ingresses were not its problem.

The fix was one line per manifest. The lesson is that Ingress feels like declarative magic until you remember it is a contract between two systems, and the contract has to match exactly.

Gateway API is the successor. It splits the single Ingress concept into Gateway, HTTPRoute, and GatewayClass, which makes multi-team ownership cleaner. The mental model is the same. A resource describes intent. A controller turns it into traffic.

One door, many rooms, and a doorkeeper who only opens the doors with their name on them.

Key takeaway

Ingress gives you one external HTTP entry point and routes by host and path to internal Services. The resource is just config. An Ingress Controller has to actually pick it up and serve it, and getting the class label wrong is how teams ship 404s.

Originally posted on LinkedIn. View original.


All Rights Reserved.