Skip to main content

Ingress and the Gateway API

What This Concept Is

Services give you L4 routing inside the cluster. For L7 (HTTP/HTTPS) routing from the outside -- host-based virtual hosts, path rewrites, TLS termination -- Kubernetes has two APIs:

  • Ingress (networking.k8s.io/v1): the original L7 API. A single resource describes rules that map (host, path) pairs to backend Services. The Ingress API has been frozen since Kubernetes v1.19; no new features are being added. Implementation is by a separately-installed Ingress controller (NGINX, Traefik, HAProxy, a cloud ALB, etc.).
  • Gateway API (gateway.networking.k8s.io): the successor. Splits responsibilities across GatewayClass, Gateway, and route kinds (HTTPRoute, GRPCRoute, TLSRoute, TCPRoute). Supports richer routing, cross-namespace references, and more expressive TLS handling. Still the newer of the two; many clusters run both.

Both depend on an external load balancer or node port to actually receive traffic from outside.

Why It Matters Here

Exposing a single API service behind a hostname is the most common real need when deploying to Kubernetes. If you try to use a LoadBalancer Service per microservice, you end up with dozens of cloud LBs, no shared TLS termination, and no way to route by path. Ingress/Gateway collapses all of that to one external entry point.

The common failure modes:

  • Ingress created but no controller installed -> no traffic ever flows; the status.loadBalancer stays empty.
  • ingressClassName missing and multiple controllers installed -> a random one (or none) will reconcile.
  • TLS secret in the wrong namespace -> 404 or TLS handshake failure.
  • Path rules using pathType: Exact when the app needs Prefix.

Concrete Example

Ingress for two services, one host:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: ["app.example.com"]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80

Equivalent Gateway API:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public
spec:
gatewayClassName: nginx
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
certificateRefs:
- name: app-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app
spec:
parentRefs:
- name: public
hostnames: ["app.example.com"]
rules:
- matches: [{ path: { type: PathPrefix, value: /api } }]
backendRefs: [{ name: api, port: 80 }]
- matches: [{ path: { type: PathPrefix, value: / } }]
backendRefs: [{ name: web, port: 80 }]

Common Confusion / Misconception

"Installing Ingress means installing a cluster feature."

It means installing a controller. The Ingress API type is built in; nothing routes traffic until a controller Deployment exists and is reconciling Ingress resources. On a fresh cluster, an Ingress object sits with empty status.loadBalancer forever.

A second confusion: "Gateway API replaces Services." It does not. An HTTPRoute's backendRefs still point at Services. Gateway replaces Ingress; the Service/Pod layer is unchanged.

A third confusion: "Ingress does rate limiting / auth / WAF." The core Ingress API does not. Controllers add those features via annotations (NGINX annotations, Traefik middlewares). When you rely on those, you have taken a dependency on a specific controller, not on Kubernetes. Gateway API addresses this with typed filters, but the ecosystem is still stabilizing.

How To Use It

The full external path:

  1. Pick one Ingress controller or Gateway implementation per cluster.
  2. Set ingressClassName or gatewayClassName explicitly on every resource.
  3. Keep TLS Secrets in the same namespace as the Ingress/HTTPRoute unless you deliberately share.
  4. For new work, prefer Gateway API; for existing clusters with stable Ingress, migrate only when you hit the Ingress API's limits.

Check Yourself

  1. What does an Ingress resource do if no Ingress controller is installed?
  2. What is the relationship between Gateway, GatewayClass, and HTTPRoute?
  3. Why is Ingress' API "frozen," and what are you expected to use for new features?

Mini Drill or Application

On a kind cluster, install the NGINX Ingress controller. Deploy two Services (web and api). Write one Ingress that routes / to web and /api to api. Port-forward the controller and verify both paths work. Then convert the same configuration to an HTTPRoute backed by a Gateway and compare the fields.

Picking an Implementation

Ingress and Gateway API are specifications; behavior comes from the controller. Choosing is a Cluster 3 architectural decision:

ControllerStrengthsWatch out for
NGINX Ingressubiquitous, huge annotation set, battle-testedconfig reloads can disrupt long-lived connections; annotations are not portable
Traefiknative Gateway API support, simple configsmaller community than NGINX
HAProxy Ingressstrong performance, rich load-balancingfewer L7 features out of the box
Istio / Envoy Gatewayfirst-class Gateway API, mesh integrationmore moving parts; only worth it if you want the mesh
Cloud LB (ALB, GKE Gateway, AKS AGIC)managed, integrates with cloud IAMcloud-specific; migrating clusters gets painful

If in doubt for a greenfield cluster, start with an NGINX or Traefik controller exposing Gateway API; migrate Ingress resources to HTTPRoutes as you touch them.

Read This Only If Stuck