03-networking-traffic

External Traffic: Gateway API and Where Ingress Stands

The Ingress API is frozen but alive; the ingress-nginx controller is archived and unpatched. Untangling the two, and learning Gateway API.

September 13, 2026
kubernetesgateway-apiingresshttprouteingress-nginxtlscert-manager

Read This Before You Follow a Tutorial

This is the area of Kubernetes where existing material is most likely to mislead you, and the stakes are unusually concrete: a very large share of blog posts, courses and Stack Overflow answers tell you to install a controller that no longer receives security patches.

Two separate things are often conflated. Keep them apart:

  • The Ingress API — a Kubernetes API object. Frozen, but not deprecated or removed. Your existing Ingress manifests keep working.
  • The ingress-nginx controller — one community implementation of that API. Archived and end-of-life.

The API is fine. That specific controller is not.

What "Frozen" Means for Ingress

The Kubernetes documentation states the position directly. It recommends using Gateway instead of Ingress, and explains that the Ingress API has been frozen — meaning it remains generally available with the usual stability guarantees, the project has no plans to remove it, and it is no longer being developed, with no further changes or updates.

So Ingress isn't going away. It simply stopped evolving, which is why anything it can't express natively is done through controller-specific annotations — the sprawl that motivated a replacement.

The Part That Is Urgent

Separately, and much more pressing: the community ingress-nginx controller reached end of life, and its repository was archived read-only on 24 March 2026. It receives no further releases, no bug fixes, and no security patches.

Its own maintainers put it plainly: if you are not already using ingress-nginx, you should not be deploying it, and you should instead identify a Gateway API implementation and use it.

🚨

This is why "just install the nginx ingress controller" — the single most repeated instruction in Kubernetes tutorials — is now advice that puts unpatched, internet-facing infrastructure into your cluster. Existing deployments won't suddenly break, and the published images and charts remain available, but nothing will be fixed. Treat it as a migration item, not a default.

Note the name collision that causes real confusion: ingress-nginx (the archived community project, kubernetes/ingress-nginx) is a different thing from F5's NGINX Ingress Controller, which is a separately maintained product. When you read that "nginx ingress is dead," it's the former.

Gateway API

Gateway API is the current standard. It is not part of core Kubernetes — it ships as an add-on of CRDs that you install, then pair with an implementation that actually handles traffic.

Its central design idea is splitting one monolithic object into resources owned by different teams:

ResourceOwned byDescribes
GatewayClassInfrastructure providerWhich controller implements this class of gateway
GatewayCluster operatorListeners — ports, protocols, hostnames, TLS
HTTPRoute / GRPCRouteApplication developerRouting rules to backend Services

This matters operationally: the platform team owns TLS certificates and listener configuration, while application teams attach routes without needing permission to edit shared infrastructure. With Ingress, both concerns lived in one object that everyone had to share.

A Working Example

The Gateway — created once by the platform team:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: gateway-system
spec:
  gatewayClassName: envoy
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: example-com-tls
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "true"

An HTTPRoute — created by each application team:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api
  namespace: production
spec:
  parentRefs:
    - name: public
      namespace: gateway-system
  hostnames:
    - "api.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v1
      backendRefs:
        - name: api
          port: 80

Note allowedRoutes on the Gateway. The operator controls which namespaces may attach routes — cross-namespace routing is explicit and governed, rather than something that happens implicitly.

Capabilities that needed annotations under Ingress

Traffic splitting by weight — canary releases as a first-class feature:

yaml
  rules:
    - backendRefs:
        - name: api-v1
          port: 80
          weight: 90
        - name: api-v2
          port: 80
          weight: 10

Header-based routing:

yaml
  rules:
    - matches:
        - headers:
            - name: x-canary
              value: "true"
      backendRefs:
        - name: api-v2
          port: 80

Both were controller-specific annotations under Ingress, which meant your manifests were portable in name only — switching controllers meant rewriting them.

Check yourself

A colleague says 'the Ingress API is deprecated, we need to migrate everything immediately or it will stop working.' What's the accurate correction?

Choosing an Implementation

Gateway API defines the API; something must implement it. Actively maintained options include Envoy Gateway, Istio, Cilium, Traefik and kgateway, alongside cloud-provider implementations.

Questions worth asking when choosing:

  • Do you already run a service mesh? If Istio or Cilium is in the cluster, its Gateway implementation is usually the path of least resistance.
  • Managed or self-hosted? Cloud implementations provision a real load balancer; in-cluster proxies give more control and more operational surface.
  • Which conformance level does it meet? The project publishes conformance reports, and support for the extended features you rely on varies.

Migrating From Ingress

You don't need a big-bang cutover. Both can run side by side:

  1. Install the Gateway API CRDs and your chosen implementation.
  2. Create a Gateway alongside the existing Ingress controller.
  3. Move one low-risk service — create its HTTPRoute, shift DNS, verify, keep the Ingress as a fallback.
  4. Migrate the rest incrementally.
  5. Remove the old controller once nothing depends on it.

Tooling exists to convert existing Ingress resources into Gateway API resources, which handles the mechanical translation. It can't convert controller-specific annotations — those encode behaviour that has no generic equivalent, and each one needs a deliberate decision. Expect the annotations to be where the real migration work lives.

TLS and Certificates

Gateways terminate TLS by referencing a Secret containing a certificate. Managing those by hand doesn't scale, so cert-manager is the near-universal answer: it obtains certificates from an issuer such as Let's Encrypt, stores them as Secrets, and renews them before expiry.

yaml
    tls:
      mode: Terminate
      certificateRefs:
        - name: example-com-tls    # a Secret cert-manager maintains

mode: Terminate decrypts at the gateway and forwards plaintext inside the cluster. mode: Passthrough forwards encrypted traffic to the backend, which is what you want when the application must see the original TLS session — mutual TLS, for example.

Check yourself

Why does Gateway API split routing into Gateway and HTTPRoute rather than using one object like Ingress?

What's Next

Traffic reaches your services from outside. The next guide covers restricting traffic inside the cluster — where the default is that every pod can reach every other pod, and where a policy can be accepted by the API server and silently do nothing at all.