Text size
On My Radar6 min read

Goodbye ingress-nginx, hello Gateway API

The most popular way into a Kubernetes cluster is retired. What that means, what Gateway API is, and how I'd move over without a weekend of downtime.

Goodbye ingress-nginx, hello Gateway API
AI-generated editorial illustration

For years, the first thing I installed on almost every new Kubernetes cluster was ingress-nginx. Before the apps, before the monitoring, before anything interesting. It was the front door. Traffic came in, ingress-nginx sent it to the right place, and I rarely had to think about it.

That front door is now officially unmaintained. In November 2025, the Kubernetes project announced that ingress-nginx would be retired, with best-effort maintenance only until March 2026. That date has passed. If you're still running it, and a lot of clusters are, here's what that means and how I'd get out.

What actually happened

ingress-nginx was a community project, kept alive for years by a very small group of volunteers in their spare time. That's not a criticism. It's the uncomfortable truth behind a lot of the software we all depend on.

Security made it worse. In March 2025, a set of serious vulnerabilities nicknamed IngressNightmare showed that a bug in ingress-nginx could, in the worst case, let an attacker take over a whole cluster. Fixing that kind of thing properly takes time and people the project simply didn't have. So Kubernetes SIG Network and the Security Response Committee made the honest call: retire it. A planned successor, a project called InGate, never got far enough along and is being retired too.

Two things are easy to mix up here, so let me be clear:

  • The ingress-nginx controller is retired. No new releases, no bug fixes, no security patches. The repositories are read-only.
  • The Kubernetes Ingress API is not. The Ingress resource still works. It's just frozen: no new features are coming.
  • The commercial NGINX Ingress Controller from F5 is a different product. Same family name, separate code, still maintained.

Do I have to panic?

No. Your cluster didn't break when the maintenance ended. Existing installs keep running, and the images and Helm charts are still available.

But think about what this component is. It's the one thing in your cluster that faces the internet directly. It parses every request from strangers. And from now on, when the next vulnerability is found, nobody will fix it. That's not a "someday" problem. It's a "this quarter" problem.

Running unpatched software at the edge of your cluster is like leaving the front door unlocked because it has never been robbed yet.

First, find out if you're affected at all. This command, from the official announcement, lists any ingress-nginx pods across the cluster:

kubectl get pods --all-namespaces \
  --selector app.kubernetes.io/name=ingress-nginx

If that returns nothing, you can relax and maybe skip to the end. If it returns pods, keep reading.

Gateway API, in plain words

Gateway API is the official successor to Ingress. It isn't brand new, either: the core became stable back in 2023, and almost every major networking project and cloud provider supports it today.

The biggest change is that one big object becomes a few smaller ones, each owned by the right person:

  • GatewayClass: which implementation runs the traffic, for example Envoy Gateway or Cilium. Usually set up once by the platform team.
  • Gateway: the actual entry point. Which ports, which hostnames, which TLS certificates. Owned by whoever runs the cluster.
  • HTTPRoute: the routing rules for one app. "Send /api to this service." Owned by the app team, in their own namespace.

With Ingress, all of that was squeezed into one resource, and everything the spec didn't cover was hidden in annotations. Anyone who has seen an Ingress with fifteen nginx.ingress.kubernetes.io/... lines knows what I mean. Those annotations only worked with one controller, so "portable Kubernetes" stopped at the front door.

Ingress Gateway API
Structure One resource does everything Separate pieces for platform and app teams
Advanced features Controller-specific annotations Built into the spec: header matching, traffic splitting, redirects
Protocols HTTP and HTTPS HTTP, gRPC, TLS, and TCP/UDP with some implementations
Portability Low once annotations creep in Much better: the same YAML works across implementations
Future Frozen Where all the new work happens

Here's what it looks like in practice. First the Gateway, set up once by the platform team:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web
  namespace: infra
spec:
  gatewayClassName: eg          # e.g. Envoy Gateway
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "*.example.com"
    tls:
      mode: Terminate
      certificateRefs:
      - name: example-com-tls
    allowedRoutes:
      namespaces:
        from: All               # tighten this with a label selector later

Then each app team attaches its own route, in its own namespace:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: shop
spec:
  parentRefs:
  - name: web
    namespace: infra
  hostnames: ["shop.example.com"]
  rules:
  - matches:
    - path: { type: PathPrefix, value: /api }
    backendRefs:
    - name: shop-api
      port: 8080

The app team never touches the Gateway, and the platform team never has to approve a routing change for someone else's service. That separation is what I like most. It matches how teams actually work.

Choosing what replaces it

Gateway API is only a specification. You still need something to run it. The good news is there are plenty of solid options:

  • Envoy Gateway: a clean, focused choice if you just want a good front door. My default suggestion for most teams.
  • Cilium: makes sense if you already use Cilium for cluster networking. One less component.
  • Istio: if you already run a service mesh, or plan to.
  • NGINX Gateway Fabric or Traefik: if your team wants to stay close to what it already knows.
  • Your cloud provider's Gateway: GKE, AWS and Azure all have managed options, which means one less thing to patch.

Don't spend weeks comparing benchmarks. For most clusters, the honest deciding question is: what does my team already understand, and what will we still enjoy maintaining in three years?

How I'd migrate, step by step

The trick is to never do a big switch-over. Run both side by side, move traffic slowly, and keep a way back.

  1. Take stock. List every Ingress and, more importantly, every annotation in use. This one-liner gives you a quick count:

bash kubectl get ingress -A -o yaml \ | grep -o 'nginx.ingress.kubernetes.io/[a-z-]*' \ | sort | uniq -c | sort -rn

The annotations are where the real work is hiding.

  1. Install the new implementation next to the old one. It gets its own load balancer and its own IP. Nothing changes for users yet.

  2. Convert with ingress2gateway. The Kubernetes project's own tool reached version 1.0 in March 2026. It reads your existing Ingress resources and writes the Gateway API equivalents:

bash ingress2gateway print --providers=ingress-nginx \ --all-namespaces -o yaml > gateway-resources.yaml

Treat the output as a first draft, not the final answer. Review it like you'd review a pull request.

  1. Fix by hand what the tool can't. Custom snippets, authentication, rate limiting, unusual rewrites and regex paths usually need manual work. Many of these become implementation-specific policies instead of annotations.

  2. Test before DNS changes. Point requests directly at the new IP and check everything works:

bash curl --resolve shop.example.com:443:NEW_GATEWAY_IP \ https://shop.example.com/api/health

  1. Move traffic gradually. Lower your DNS TTLs a day before, then move one hostname at a time, starting with the least important. Keep ingress-nginx running so rolling back is just a DNS change.

  2. Clean up. Once everything has run quietly for a while, remove ingress-nginx and its Ingress resources. Don't forget the tools around it: cert-manager and ExternalDNS both support Gateway API, but they need to be configured for it.

What tends to bite

A few things are easy to underestimate:

  • Snippet annotations. If someone put raw NGINX configuration into configuration-snippet years ago, there's no direct translation. You first need to work out what it was even for. Sometimes the answer is "nothing anymore."
  • Rewrites. rewrite-target with regex capture groups doesn't map one to one. Test every rewritten path.
  • Small behavior differences. Default timeouts, body size limits and header handling differ between implementations. Things that "just worked" may suddenly return a 413 or a timeout.
  • Who owns what. The new model splits responsibility between teams. That's great, but only if you've agreed who owns the Gateway and which namespaces may attach routes to it.

What I'd tell someone starting today

If you're building a new cluster, don't install ingress-nginx, and don't write new Ingress resources either. Start with Gateway API from day one. The concepts take an afternoon to learn, and you'll never have to do this migration.

And if you're learning Kubernetes, this is a good lesson in itself. The most popular project in its category, used by almost everyone, was maintained by a handful of tired volunteers. Before you build on something, it's worth asking who is actually keeping it alive.

So, is it worth the effort?

Honestly, yes, and not only for security. Gateway API is simply the better model. Cleaner YAML, fewer magic annotations, a clear split between platform and app teams, and features that used to need hacks are now built in.

The retirement just forces a move most of us should have made anyway. Better to do it calmly now than in a hurry the day the next big vulnerability is announced.

My take: the front door changed, but the job didn't. Plan it, run both side by side, move slowly, and keep a way back.

— Desly