Infrastructure3 min read

Ingress-NGINX Is Retired: A Practical Migration to the Kubernetes Gateway API

The community ingress-nginx controller reached end of maintenance in March 2026. If it's still routing your production traffic, you're running an unpatched edge proxy. Here's how the Gateway API differs from Ingress, and a pragmatic path to migrate.

Gopal Yendluri
Series: Kubernetes and Cloud Networking · Part 4 of 5
  1. Kubernetes on EC2 Before EKS: What We Got Wrong (And Right)
  2. Common Kubernetes Pitfalls on EC2 (And How to Avoid Them)
  3. Istio Ambient Mode: A Service Mesh Without the Sidecar Tax
  4. Ingress-NGINX Is Retired: A Practical Migration to the Kubernetes Gateway API
  5. Ingress, Istio Service Mesh and API Gateway Explained: What Each Does and When to Use It
Contents
  1. Why This Can't Wait
  2. Ingress vs Gateway API
  3. What It Looks Like
  4. A Pragmatic Migration Plan
  5. Gotchas
  6. The Takeaway

Why This Can't Wait

In November 2025, Kubernetes SIG Network and the Security Response Committee announced the retirement of the community ingress-nginx controller, with best-effort maintenance ending in March 2026. After that: no releases, no bug fixes, and no security patches. Existing installations keep working, which is exactly what makes this dangerous. The internet-facing proxy in front of your cluster is quietly becoming the least-maintained software you run.

The recommended path is the Kubernetes Gateway API. (Note: this is about the community kubernetes/ingress-nginx project, not F5's separate NGINX Ingress Controller.)

Ingress vs Gateway API

The Ingress resource was deliberately minimal: hosts, paths, TLS. Everything else, including rewrites, timeouts, canaries, header matching and rate limits, was bolted on through controller-specific annotations. Configuration became unportable strings like nginx.ingress.kubernetes.io/canary-weight: "20".

The Gateway API replaces this with typed, role-oriented resources:

Resource Owned by Purpose
GatewayClass Infrastructure provider Which implementation (Envoy Gateway, Istio, Cilium, NGINX Gateway Fabric, AWS, GKE, etc.)
Gateway Platform team Listeners: ports, protocols, hostnames, TLS certificates
HTTPRoute / GRPCRoute Application teams Routing rules for their services
ReferenceGrant Resource owner Explicit permission for cross-namespace references

That split is the real improvement. The platform team owns the shared entry point and its certificates; application teams attach routes from their own namespaces without touching shared config.

What It Looks Like

A shared Gateway, owned by the platform team:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: gateway-infra
spec:
  gatewayClassName: eg            # e.g. Envoy Gateway
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        certificateRefs:
          - name: wildcard-example-com
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: public

A route, owned by the application team, including a weighted canary with no annotations:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop-api
  namespace: shop
spec:
  parentRefs:
    - name: public
      namespace: gateway-infra
  hostnames: ["api.example.com"]
  rules:
    - matches:
        - path: { type: PathPrefix, value: /v2 }
      backendRefs:
        - name: api-stable
          port: 80
          weight: 90
        - name: api-canary
          port: 80
          weight: 10
      timeouts:
        request: 10s

Header-based routing, request and response header modification, redirects, URL rewrites, request mirroring and timeouts are all first-class fields.

A Pragmatic Migration Plan

1. Inventory. List every Ingress and, more importantly, every annotation in use. Annotations are where migrations go wrong: custom snippets, auth annotations, rewrite rules and rate limits all need an equivalent.

kubectl get ingress -A -o json \
  | jq -r '.items[].metadata.annotations // {} | keys[]' \
  | sort | uniq -c | sort -rn

2. Choose an implementation. Options include Envoy Gateway, Istio, Cilium, Kong, Traefik, NGINX Gateway Fabric, kgateway, and the cloud-native controllers (the AWS Gateway API controller, GKE Gateway). Pick based on what you already run: if you're adopting Istio, use its Gateway API support; on EKS, consider whether you want an in-cluster proxy or AWS load balancers.

3. Convert with ingress2gateway, then review. The ingress2gateway tool (1.0 was released in March 2026) translates Ingress resources and common ingress-nginx annotations into Gateway API resources. Treat its output as a first draft and review anything it flags as unsupported.

ingress2gateway print --providers=ingress-nginx -A > gateway-resources.yaml

4. Run side by side. Deploy the new Gateway with its own load balancer. Test using a hostname override or a test DNS name. Compare status codes, headers, redirects, body sizes, timeouts and TLS behaviour. Pay special attention to anything that relied on NGINX defaults: max body size, proxy timeouts, and path-matching subtleties (regex paths and trailing slashes).

5. Shift traffic gradually. Use weighted DNS to move a small percentage of traffic, watch error rates and latency, then complete the cutover. Keep the old controller running, unexposed, for a short rollback window.

6. Clean up and codify. Remove the old controller and its load balancer, and add a policy check that rejects new Ingress resources so nobody reintroduces them.

Gotchas

  • Snippet annotations have no direct equivalent, by design. Arbitrary NGINX config injection was a security problem. Find the typed equivalent or an implementation-specific policy resource.
  • Authentication and rate limiting are implementation-specific policy resources today. Expect some vendor-specific YAML for these.
  • Default behaviours differ. Body-size limits, timeouts and header handling won't necessarily match NGINX defaults. Test with real traffic patterns, including large uploads and long-running requests.
  • Certificates. If you use cert-manager, it supports issuing certificates for Gateway listeners; update your issuers accordingly.

The Takeaway

If ingress-nginx is still in your cluster, treat it as a security issue with a deadline that has already passed. The Gateway API is a genuine upgrade, not just a replacement: typed routing features, clean separation between platform and application teams, and a standard that service meshes and cloud providers now share. Inventory your annotations, convert with ingress2gateway, run side by side, and shift traffic gradually.

Next in Kubernetes and Cloud Networking
Ingress, Istio Service Mesh and API Gateway Explained: What Each Does and When to Use It
KubernetesGateway APIIngressingress-nginxnetworkingmigration