A Kubernetes Ingress is a set of HTTP routing rules that lets many services share a single entry point into the cluster. Instead of giving every public service its own LoadBalancer, you put one load balancer in front of an Ingress controller, and the controller routes each request by hostname and path to the right Service. The same layer usually terminates TLS, so certificates live in one place instead of inside every application.
This guide covers why Ingress exists, how the Ingress object differs from the Ingress controller, how IngressClass selects a controller, how host and path routing work, and how to automate HTTPS with cert-manager. It also covers what Ingress can't do and where it sits next to the newer Gateway API.
Why use Kubernetes Ingress instead of many LoadBalancers
Suppose you have 10 services that need to be reachable from the internet. If you expose each one as type: LoadBalancer, you get 10 external load balancers and 10 public IPs. On a cloud provider, each one is a billed resource. On bare metal, each one takes an address from the pool that MetalLB (or a similar tool) manages, and those pools are usually small.
With Ingress you run one load balancer, pointed at the Ingress controller, and the controller fans traffic out to all 10 services based on rules:
| Approach | External IPs | TLS certificates | Routing logic |
|---|---|---|---|
One LoadBalancer per service | One per service | Managed in each app or LB | None, one IP maps to one service |
| Ingress | One (or a few) | Centralized at the Ingress | Host, path, and controller-specific rules |
If you're unsure how ClusterIP, NodePort and LoadBalancer relate to each other, read Kubernetes Service types explained first. Ingress builds on top of them.
Ingress object vs Ingress controller
This is the part that confuses most people. There are two separate things:
- The Ingress object is configuration only. It is a resource in the
networking.k8s.io/v1API that says "requests forapi.example.com/v1go to Serviceapion port 80". Creating one on a cluster with no controller does nothing at all. - The Ingress controller is a reverse proxy running as pods in the cluster, such as Traefik, HAProxy Ingress, the F5 NGINX Ingress Controller, or a cloud controller like the AWS Load Balancer Controller. It watches Ingress objects through the API server and reconfigures itself whenever they change.
The request path looks like this:
client -> external load balancer -> Ingress controller pods -> Service -> PodsOnce the controller has picked a backend, it usually sends traffic straight to pod IPs taken from the Service's EndpointSlices, not through the Service's virtual IP. Either way, only pods that pass their readiness probe receive traffic.
A note on controller choice: the community ingress-nginx project (kubernetes/ingress-nginx) was retired, and its best-effort maintenance ended in March 2026. Existing installs keep running, but they no longer get fixes or security patches. If you're starting fresh, pick an actively maintained controller or go straight to a Gateway API implementation. The Ingress API itself is still stable and supported.
IngressClass: choosing which controller handles an Ingress
A cluster can run more than one controller. IngressClass tells Kubernetes which controller owns which Ingress, much like a StorageClass selects a storage provisioner.
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: public
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: traefik.io/ingress-controllerAn Ingress refers to it with spec.ingressClassName. The old kubernetes.io/ingress.class annotation is deprecated, so use the field instead. An Ingress with no class goes to the default class if one is marked, and otherwise may be ignored.
A common pattern is two classes backed by two controller deployments:
publicfor internet-facing traffic, behind an external load balancer.internalfor tools and admin UIs, reachable only from the corporate network or VPN.
Separate classes also spread load. Each controller has its own replicas and its own load balancer, so one busy domain doesn't crowd out the rest.
Host-based and path-based routing
Ingress routes on two things: the Host header and the URL path.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
namespace: shop
spec:
ingressClassName: public
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- host: www.example.com
http:
paths:
- path: /static
pathType: Prefix
backend:
service:
name: static-files
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 3000- Host-based:
api.example.comandwww.example.comgo to different services, even though both resolve to the same IP. - Path-based: under
www.example.com,/staticgoes to one service and everything else goes toweb.
pathType matters more than it looks:
| pathType | Behavior |
|---|---|
Exact | Matches the path exactly, /api but not /api/. |
Prefix | Matches by path segment. /api matches /api and /api/users but not /apiv2. |
ImplementationSpecific | Up to the controller. Avoid it unless you need controller-specific matching. |
When several paths match, the longest match wins, so /static beats / regardless of their order in the file. Backend Services must be in the same namespace as the Ingress.
TLS termination on Kubernetes Ingress
Ingress usually terminates HTTPS. The controller holds the certificate, decrypts the request, and forwards plain HTTP to the service inside the cluster. Applications don't need to know about certificates at all.
The certificate is stored in a Secret of type kubernetes.io/tls in the same namespace as the Ingress, and the Ingress references it in spec.tls:
spec:
ingressClassName: public
tls:
- hosts:
- api.example.com
- www.example.com
secretName: shop-tls
rules:
# ... same rules as aboveThe controller uses SNI to pick the right certificate when one Ingress controller serves many domains. Most controllers also redirect HTTP to HTTPS when TLS is configured, but how you turn that on or off is controller-specific.
Plain HTTP inside the cluster is a deliberate trade-off. If you need encryption on every hop, re-encrypt to the backend (controller-specific) or use a service mesh with mTLS.
Automating certificates with cert-manager
Renewing certificates by hand eventually fails, usually on a weekend. cert-manager watches Ingress objects, requests certificates from an ACME issuer such as Let's Encrypt, stores them in the Secret you named, and renews them before they expire.
First create an issuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
ingressClassName: publicThen annotate the Ingress:
metadata:
name: shop
namespace: shop
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prodcert-manager sees the tls block, creates a Certificate resource, solves the HTTP-01 challenge through the same Ingress controller, and writes the result to shop-tls. Check progress with:
kubectl get certificate -n shop
kubectl describe certificate shop-tls -n shopUse the Let's Encrypt staging server while testing, because the production endpoint has rate limits. For wildcard certificates you need the DNS-01 solver, since HTTP-01 can't prove control of *.example.com.
L7 features, and what Ingress can't do
Because Ingress works at Layer 7, the controller can read HTTP host, path and headers. On top of basic routing, most controllers offer:
- HTTP to HTTPS redirects and HSTS
- Path rewrites and URL prefix stripping
- Rate limiting and IP allowlists
- External authentication (forward auth to an SSO service)
- Canary or weighted routing between two versions
- Request size limits, timeouts, and CORS headers
The catch is that almost all of these come from controller-specific annotations or CRDs, not the Ingress spec. An annotation that works on Traefik means nothing to HAProxy. Keep those settings in one place so a controller migration doesn't become an archaeology project.
Ingress also has hard limits:
- HTTP and HTTPS only. Raw TCP or UDP, such as a database, MQTT broker or game server, needs a
LoadBalancerorNodePortService, or a controller-specific TCP feature. - Header matching and traffic splitting aren't in the spec. They depend on the controller.
- One resource mixes concerns. The platform team owns the listener and TLS, and app teams own routes, but both live in the same object.
The Gateway API (gateway.networking.k8s.io) addresses all three. It splits the model into GatewayClass, Gateway (listeners and TLS, owned by the platform team) and HTTPRoute (routes, owned by app teams), and it standardizes header matching and weighted backends. For new platforms it's the direction to take. Ingress remains fine for straightforward host and path routing.
Is the Ingress controller a bottleneck?
"Everything goes through one Ingress" sounds like a single point of failure, but it doesn't have to be one:
- The controller is logically one, physically many. Run it as a Deployment with several replicas, or as a DaemonSet, spread across nodes. The external load balancer distributes across those pods.
- Autoscale it. An HPA on the controller Deployment works the same way it does for your apps. See Kubernetes autoscaling with HPA, VPA and Cluster Autoscaler.
- Split by IngressClass for heavy domains or for public and internal traffic.
- Only north-south traffic passes through it. Service-to-service calls inside the cluster go directly over
ClusterIPand never touch the Ingress. In most microservice systems, that is the bulk of the traffic.
For DNS, anycast, and multiple load balancers in front of several Ingress controllers, see multi-layer load balancing with DNS, anycast and Ingress.
FAQ
What is the difference between Ingress and an Ingress controller?
The Ingress is a Kubernetes object that holds routing rules. The Ingress controller is the proxy software that reads those rules and actually serves traffic. Without a controller, Ingress objects have no effect.
Do I still need a LoadBalancer Service if I use Ingress?
Usually yes, but only one. The Ingress controller itself is exposed through a LoadBalancer (or NodePort behind an external load balancer), and all your apps sit behind it as ClusterIP Services.
Can Kubernetes Ingress route TCP or UDP traffic?
Not through the standard spec. Ingress is for HTTP and HTTPS. Use a LoadBalancer Service, a controller-specific TCP feature, or Gateway API routes for TCP and UDP.
Should I use Ingress or Gateway API in 2026?
For new platforms, Gateway API is the better long-term choice because it standardizes features that Ingress leaves to annotations. Existing Ingress setups are still supported. The priority is moving off retired controllers, not off the Ingress API.
Why is my cert-manager certificate stuck in "not ready"?
Run kubectl describe on the Certificate, then on its Order and Challenge. Common causes are DNS not pointing at the Ingress yet, the wrong ingressClassName on the HTTP-01 solver, or hitting Let's Encrypt production rate limits.
Ingress checklist
- One controller per traffic class (
public,internal), each with 2+ replicas spread across nodes. - Every Ingress sets
ingressClassNameexplicitly. - TLS via cert-manager, tested against the staging issuer first.
Prefixpaths, with no reliance on rule order.- Controller-specific annotations kept to a minimum and documented.
- A plan for Gateway API if you run the retired
ingress-nginx.
Get these right and one entry point can serve dozens of services without becoming a bottleneck. If you want hands-on practice with Ingress, TLS and the rest of the Kubernetes networking stack, Vectorkub runs free DevOps courses.
