Ingress
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Services give a stable virtual IP inside the cluster; Ingress is the API
that exposes HTTP and HTTPS traffic from outside the cluster to those Services, adding host- and path-based
routing and TLS termination in front of them without a separate LoadBalancer Service per backend. This page covers
the Ingress API as it stands in Kubernetes 1.37 — frozen, but still supported — and what its freeze and the
retirement of the most widely used controller, ingress-nginx, mean for a cluster running it today. It follows on
from Services and DNS
and Service Discovery, and precedes Gateway API, the API the project now
recommends for new work.
The Ingress API
An Ingress object (networking.k8s.io/v1, GA since 1.19) is a set of routing rules that a separately-installed
Ingress controller reads and turns into real load-balancer or reverse-proxy configuration; creating an Ingress
object on its own routes no traffic at all; it requires a running controller. Each rule under spec.rules can match
an optional host (omitted, a rule applies to all inbound traffic that no more specific host matches) and a list of
HTTP paths, each pairing a path against a backend — a Service name and port:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bookshelf
annotations:
cert-manager.io/cluster-issuer: bookshelf-issuer
spec:
ingressClassName: nginx
tls:
- hosts:
- bookshelf.example.com
secretName: bookshelf-tls
rules:
- host: bookshelf.example.com
http:
paths:
- path: /catalog
pathType: Prefix
backend:
service:
name: catalog-api
port:
number: 80
- path: /reviews
pathType: Prefix
backend:
service:
name: reviews-api
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
defaultBackend:
service:
name: web
port:
number: 80
Source: Kubernetes Docs — Ingress.
Every path carries a pathType, which decides how it is matched against the request path:
pathType |
Matching rule |
|---|---|
|
Matches the URL path exactly, case-sensitively, including trailing slash differences. |
|
Matches on a |
|
Matching is left entirely to the Ingress controller, which may treat it the same as |
spec.ingressClassName selects which installed controller should act on the object, by the name of an
IngressClass resource; a cluster with exactly one IngressClass marked default
(ingressclass.kubernetes.io/is-default-class: "true") applies it automatically to any Ingress that omits the
field, which is convenient for a single-controller cluster but silently ambiguous the moment a second controller (or
a second IngressClass for the same controller, e.g. an internal vs a public one) is installed, so Bookshelf sets
it explicitly. spec.tls lists one or more hosts together with a secretName holding a kubernetes.io/tls
Secret (a tls.crt / tls.key pair); the controller terminates TLS for the listed hosts using that certificate, and
requests for a host with no matching TLS entry fall back to the controller’s own default certificate.
spec.defaultBackend names a Service that receives any request matching no rules at all — distinct from a path:
"/" rule, which only ever catches requests that also match the rule’s host — and, when omitted, an unmatched
request gets the controller’s own 404 response instead of a Bookshelf page. Every backend, in a rule or in
defaultBackend, may reference a Service by number or by the Service’s own named port, exactly as
Services describes for multi-port Services.
kubectl apply -f bookshelf-ingress.yaml
kubectl get ingress bookshelf
kubectl describe ingress bookshelf
NAME CLASS HOSTS ADDRESS PORTS AGE
bookshelf nginx bookshelf.example.com 203.0.113.10 80, 443 3m
Source: Kubernetes Docs — Ingress.
kubectl describe ingress is the fastest way to check that a controller has actually picked up the object: its
Events section shows the controller that synced the rule (by name, matching ingressClassName), and its rendered
rule table catches a typo in a host or path before it reaches a browser. Book 1 ch. 3 describes exposing a
Service to the outside world only through a cloud load balancer, a hand-rolled HAProxy, or a firewall rule against a
NodePort Service — the book predates Ingress entirely, and everything on this page is "new since the books".
Chapter 1 (1) of Book 2 lists Ingress as one of the decoupling abstractions Kubernetes provides, alongside Pods,
Services and Namespaces, but the sampler’s five chapters never cover the object itself.
Ingress controllers
Kubernetes ships no built-in Ingress controller; the project itself maintains reference implementations for AWS
(the AWS Load Balancer Controller) and GCE, and the ecosystem page lists more than two dozen third-party
controllers — ingress-nginx, Traefik, HAProxy Ingress, Contour, Kong, Istio and Cilium’s own Ingress support among
them. Installing a controller is ordinarily a Helm chart or a single manifest that deploys the controller’s
Pods (as a Deployment or DaemonSet) plus a Service of type: LoadBalancer or NodePort that receives external
traffic and a matching IngressClass object. On kind there is no cloud load balancer to provision that Service, so
the Getting Started kind configuration maps host ports 80/443 into the
control-plane node labelled ingress-ready=true. A NodePort Service alone cannot receive that traffic, since its
port comes from the 30000-32767 range; instead the controller is pinned to that node with a nodeSelector (plus a
toleration for the control-plane taint) and bound to the node’s own ports 80/443 with hostPort, which is enough
for local testing of the manifest above. A cluster can run more than one controller side by side — for instance a public-facing one and an
internal-only one — selected per Ingress object by ingressClassName.
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.service.type=NodePort \
--set controller.hostPort.enabled=true \
--set-string controller.nodeSelector.ingress-ready=true \
--set 'controller.tolerations[0].key=node-role.kubernetes.io/control-plane' \
--set 'controller.tolerations[0].operator=Exists' \
--set 'controller.tolerations[0].effect=NoSchedule'
Source: Kubernetes Docs — Ingress Controllers and kind Docs — Ingress.
The Ingress API freeze and the Ingress-NGINX retirement
The Ingress API itself has been GA since Kubernetes 1.19 and is now frozen: the Kubernetes project has said it will
receive no further fields or behaviour changes, because ongoing API development has moved to
Gateway API. A frozen API is not a deprecated or removed one — existing
Ingress objects keep working under the normal backward-compatibility guarantees, and no removal date has been
announced — but it means Ingress will never gain the capabilities Gateway API already has, such as typed protocol
routes, weighted traffic splitting or cross-namespace routing via ReferenceGrant
(Gateway API covers all three).
Separately, and specifically, the Kubernetes project announced the retirement of ingress-nginx, the specific controller used in the example above and the most widely deployed one: its best-effort maintenance ended in March 2026, after which it receives no further releases, bugfixes or security updates. A cluster’s existing ingress-nginx installation keeps running and its published container images and Helm chart remain available, but a newly discovered vulnerability in it will not be patched, which is a materially different risk profile from an actively maintained controller. Existing clusters on ingress-nginx therefore have three options: migrate the Ingress rules to Gateway API (the path this page and Gateway API recommend), move to one of the many actively maintained third-party Ingress controllers listed above, or accept the frozen state and its growing security exposure while the rest of the migration is planned. The Ingress API itself is unaffected by this retirement — it is the retirement of one implementation of it, not of the API.
Migrating with ingress2gateway
ingress2gateway is the SIG Network project that reads existing Ingress objects (and, for several controllers
including ingress-nginx, their controller-specific annotations) and emits the equivalent Gateway API
HTTPRoute/Gateway resources, rather than requiring a hand rewrite of every rule. Its 1.0 release, timed to the
ingress-nginx retirement, extended its ingress-nginx annotation coverage from a handful of fields to more than
thirty commonly used ones — CORS, backend TLS, regex path matching and path rewriting among them — each backed by
an integration test asserting behavioural equivalence between the original ingress-nginx configuration and the
Gateway API resources it generates:
ingress2gateway print --input-file=bookshelf-ingress.yaml --providers=ingress-nginx
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: bookshelf
spec:
parentRefs:
- name: bookshelf-gateway
hostnames:
- bookshelf.example.com
rules:
- matches:
- path: {type: PathPrefix, value: /catalog}
backendRefs:
- name: catalog-api
port: 80
# ... one HTTPRoute rule per Ingress path, plus a Gateway for the TLS listener
The tool prints the generated resources (or writes them to a directory) for review rather than applying them directly, and it flags any annotation or behaviour it cannot translate so that gap is closed by hand before cutting traffic over; Gateway API's Ingress-to-Gateway mapping table lists the Gateway API equivalent for each Ingress concept covered on this page, for a migration done manually instead.
cert-manager for TLS
The example Ingress above assumes a bookshelf-tls Secret already exists with a valid certificate; in practice
that Secret is created and kept renewed by cert-manager, a separately installed operator that watches for
cert-manager.io/* annotations on Ingress objects (via its ingress-shim component) and creates a matching
Certificate custom resource on the cluster’s behalf, requesting it from a configured Issuer (namespaced) or
ClusterIssuer (cluster-wide) — commonly Let’s Encrypt via the ACME protocol, but also HashiCorp Vault or a
private PKI. cert-manager stores the resulting private key and certificate in exactly the Secret named by
spec.tls[].secretName, and renews it automatically well before expiry, without any change to the Ingress object
itself:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: bookshelf-issuer
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: platform-team@example.com
privateKeySecretRef:
name: bookshelf-issuer-account-key
solvers:
- http01:
ingress:
ingressClassName: nginx
Source: cert-manager Docs — ACME Issuer.
cert-manager also ships a CSI driver and an Istio integration that hand a Pod its certificate directly, without
going through a Secret or an Ingress annotation at all, which is the preferred route once a cluster runs a service
mesh (Cluster Networking, CNI and Service Mesh covers
mesh mTLS); for a plain Ingress-fronted cluster such as Bookshelf's, the annotation-driven flow above is
sufficient and needs no change when the routes themselves later move to Gateway API, since cert-manager’s
Gateway API support uses the same Certificate/Issuer machinery against a Gateway listener instead of an
Ingress object.