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

Every path carries a pathType, which decides how it is matched against the request path:

pathType Matching rule

Exact

Matches the URL path exactly, case-sensitively, including trailing slash differences.

Prefix

Matches on a /-separated path-element prefix; /catalog matches /catalog and /catalog/1 but not /cataloguer.

ImplementationSpecific

Matching is left entirely to the Ingress controller, which may treat it the same as Prefix or apply its own (often regex-based) rules; the Bookshelf rules above stay on Prefix for portability across controllers.

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

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'

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.

flowchart LR subgraph before["Today: Ingress"] direction LR C1([Client]) --> IC["Ingress controller\n(e.g. ingress-nginx)"] IC --> ING["Ingress: bookshelf\nrules + tls"] ING --> SVC1["Service catalog-api"] end subgraph after["ingress2gateway"] direction LR C2([Client]) --> GW["Gateway: bookshelf-gateway"] GW --> ROUTE["HTTPRoute: bookshelf"] ROUTE --> SVC2["Service catalog-api"] end before -. "ingress2gateway print" .-> after

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

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.

References