Ingress, Gateway API and exposing applications
Put HTTP traffic into the cluster: Ingress resources and controllers, host and path routing, TLS termination, and when the Gateway API earns its extra objects.
An Ingress object is only half the story
Ingress is a routing rule expressed as a Kubernetes object. On its own it does nothing. You also need an Ingress controller — a Deployment inside the cluster that watches Ingress objects and programs a real proxy such as nginx, Envoy or Traefik.
# the controller is a normal workload; install it once per cluster
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --create-namespace
kubectl get pods -n ingress-nginx
kubectl get ingressclass
kubectl get svc -n ingress-nginx # the LoadBalancer the whole world hits| Approach | Layer | Cost and caveats |
|---|---|---|
| ClusterIP | L4, internal only | Cheapest. Reachable only from inside the cluster |
| NodePort | L4, every node | Opens a high port on every node; you manage the load balancer yourself |
| LoadBalancer | L4, one address per Service | One cloud LB per service gets expensive fast; no HTTP awareness |
| Ingress | L7, shared entry point | One address for many hosts and paths, with TLS. Needs a controller |
| Gateway API | L7, shared entry point | Same idea with typed roles and richer matching |
Hosts, paths and TLS
An Ingress matches on host and path, then forwards to a Service and port. Everything else — rewrites, timeouts, body size, sticky sessions — is expressed through controller-specific annotations, which is the main reason the Ingress API is being superseded.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: storefront
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts: ["shop.example.com"]
secretName: shop-tls # the TLS certificate as a Secret
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
- host: blog.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: blog
port:
number: 80pathType: Prefixmatches on path segments, not raw string prefixes:/apidoes not match/apifoo. UseExactwhen you mean an exact path.- The longest matching path wins, so order of rules does not matter.
- TLS certificates are Secrets. Cert-manager can issue and renew them from an annotation, which removes the main operational chore.
- A request that matches no rule goes to the controller's default backend and returns 404.
- The Service behind an Ingress still needs working endpoints — an Ingress cannot route to pods that are not Ready.
kubectl get ingress
kubectl describe ingress storefront # shows each rule and the resolved backend
kubectl get endpoints api # an ingress with no endpoints returns 503The Gateway API
The Gateway API splits one Ingress object into three roles: infrastructure teams own the GatewayClass and Gateway, application teams own HTTPRoute objects in their own namespace. Matching is typed instead of annotation-driven, so the same route means the same thing on every implementation.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gateway
namespace: infra
spec:
gatewayClassName: envoy
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: "*.example.com"
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: storefront
spec:
parentRefs:
- name: shared-gateway
namespace: infra
hostnames: ["shop.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /api
- headers:
- name: x-canary
value: "true"
backendRefs:
- name: api-canary
port: 8080
weight: 10
- name: api
port: 8080
weight: 90- Header matching and traffic weighting are first-class in
HTTPRoute, which is exactly what you want for canary releases. - A route is accepted or rejected per listener, and the status conditions on the route explain why. Read them before suspecting the proxy.
- Gateway API ships
TCPRoute,TLSRouteandGRPCRoute, so non-HTTP traffic has a typed answer too. - Adoption depends on the controller. Check that your implementation supports the version and features you need before migrating.
FAQ
I created an Ingress and get 404. Where do I look?
kubectl get ingressclass) and that your Ingress names it. Then check the Service name and port, and that the Service has endpoints. Finally read the controller's own logs, which show the routing table it built.Ingress or Gateway API for a new cluster?
Related
Networking, DNS and NetworkPolicy Cluster architecture and a local setup
Last refreshed 2026-09-18.