Kubernetes networking

Kubernetes Ingress NGINX End of Life: Now What?

Kubernetes retired the ingress-nginx controller in March 2026. Clusters that still run it keep routing traffic, but nobody will patch the next vulnerability. This guide covers what changed, how to check your exposure, how to convert Ingress resources, and how the migration works on SeaGit clusters.

By , Founder at SeaGit

·

Key takeaways

  • Ingress NGINX is retired. Best-effort maintenance ended in March 2026, and the repository is archived and read-only.
  • Existing clusters keep working after retirement. The risk is the next vulnerability, which no release will fix.
  • Check with one command: kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx.
  • Kubernetes recommends Gateway API. None of the alternatives is a drop-in replacement, so plan engineering time.
  • ingress2gateway 1.0 converts most common ingress-nginx annotations. Review its warnings, because regex matching and timeouts are translated with defaults you may need to change.

Is ingress-nginx really end of life?

Yes. The retirement was announced on 11 November 2025, and the Kubernetes Steering and Security Response Committees restated it on 29 January 2026. The project stopped releasing bugfixes and security patches after March 2026. Existing installations keep running, so nothing breaks on the retirement date. The change is that no one is committed to fixing the next vulnerability in the controller.

What was announced

The first notice came from SIG Network and the Security Response Committee in November 2025. It set the end of best-effort maintenance at March 2026 and said that afterwards there would be no releases, no bugfixes and no updates for newly found vulnerabilities. The notice also said existing deployments would keep working and that the Helm charts and container images would remain available.

The January 2026 statement from the Steering and Security Response Committees is blunter. It says ingress-nginx is critical infrastructure for about half of cloud native environments, citing internal Datadog research. It also says the project had only one or two maintainers, working in their free time, for several years. That is the reason the committees gave for winding it down rather than finding a new maintainer team. The statement puts the risk in plain terms: remaining on the controller after retirement "leaves you and your users vulnerable to attack."

The retirement applies to the ingress-nginx controller. It does not apply to the Ingress resource itself, and it does not apply to the NGINX Ingress Controller that F5 maintains under a different repository. If you are unsure which one you run, check the image and the Helm chart name on the running workload.

What end of life means in practice

The project README is the most precise statement, so here it is in summary. Before the cutoff, there was best-effort maintenance. After the cutoff there are no further releases of any kind, no bugfixes, and no security updates. The README also notes that the repository was archived by its owner on 24 March 2026 and is now read-only.

Keeps working

  • Clusters that already run the controller keep routing traffic.
  • Helm charts and container images stay available to pull.
  • Ingress resources you already have keep being served by the controller.

Stops

  • Any release, including a security release.
  • Fixes for vulnerabilities found after the cutoff.
  • Updates of any kind, which the statement says cannot be left to the last minute.

The practical effect is timing. A cluster does not change on the retirement date, so teams often do nothing until a scanner or an advisory names the controller. At that point the choice is between an emergency migration and an unpatched component. The Kubernetes statement says that existing deployments may not be noticed until they are compromised, which is why the first step is to find out what you run.

Check whether your cluster runs it

The Kubernetes statement gives this check: find ingress-nginx pods with a cluster administrator account. It is the fastest way to answer the question, and it needs no changes to the cluster.

shell
kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx
The check from the Kubernetes statement. Any output means at least one ingress-nginx controller pod is running.

An empty result does not prove you are clear. Controllers installed with a different label, a Helm release renamed by your team, or a second controller in another namespace can all escape that selector. The next two commands show which IngressClass and which Ingress resources exist, so you can match each route to its controller.

shell
# Which controller implements each IngressClass?
kubectl get ingressclass -o yaml

# Which Ingress resources exist, and in which namespaces?
kubectl get ingress --all-namespaces
Each IngressClass has a controller field that names its implementation. Each Ingress points at a class through spec.ingressClassName.

Count the Ingress resources that point at a class served by ingress-nginx. That number is the real scope of the migration, because each one needs a new route on the replacement controller.

Pick a migration path

There are two paths the Kubernetes project endorses. They differ in how much you change at once.

Gateway API, the recommended successor

Gateway API is the successor to the Ingress API. It separates what Ingress used to combine into one object. The Gateway defines where and how traffic enters the cluster, which is a cluster operator’s task. The HTTPRoute defines how traffic reaches Services, which is an application developer’s task. The Gateway API documentation describes that split as the reason it is safer for shared clusters.

The trade-off is that Gateway API is a different API. Annotations do not carry over. Some ingress-nginx behaviour has a native field in Gateway API, some needs an implementation-specific extension, and some has no equivalent. The Gateway API guide for ingress-nginx users points to that mapping, and the rest of this article shows how to work through it.

Another maintained Ingress controller

Kubernetes also lists other Ingress controllers in its documentation. Staying on the Ingress API means fewer resources change, so this is the smaller short-term move if the goal is to close the security gap quickly. The cost is that annotations are controller-specific. The Gateway API migration guide makes this point directly: every implementation has its own extensions, so moving controllers can mean rewriting one annotation dialect into another.

Ingress keeps load balancer and routing in one object. Gateway API splits them across three resources.On the left, one Ingress object holds hosts, paths, TLS and controller-specific annotations, and the ingress-nginx controller reads it. On the right, a GatewayClass selects the implementation, a Gateway defines listeners and TLS, and an HTTPRoute holds the host and path rules that point to Services.Ingress API (before)Ingress objecthosts, paths, TLS secret, backendAnnotationsnginx.ingress.kubernetes.io/*, controller-specificingress-nginx controller (retired)Gateway API (after)GatewayClasswhich controller implements itGateway (platform team)listeners, ports, TLS certificate refsHTTPRoute (application team)hostnames, path matches, backendRefsService, then Pods
Ingress keeps load balancer and routing in one object. Gateway API splits them into a GatewayClass, a Gateway owned by the platform team and HTTPRoutes owned by application teams.

Convert with ingress2gateway

Ingress2Gateway is the Kubernetes SIG Network conversion tool. Its 1.0 release, published on 20 March 2026, supports more than 30 common ingress-nginx annotations, including CORS, backend TLS, regex matching and path rewrite. Before 1.0 it supported three. Each supported annotation is backed by integration tests that compare runtime behaviour on real controllers, not just the YAML.

Install it with Homebrew or Go, then print the converted manifests from files or from the cluster:

shell
# With Homebrew
brew install ingress2gateway

# With a Go toolchain (pins the 1.0.0 release)
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0
shell
# From manifest files
ingress2gateway print --input-file my-manifest.yaml,my-other-manifest.yaml --providers=ingress-nginx > gwapi.yaml

# From one namespace in the live cluster
ingress2gateway print --namespace my-api --providers=ingress-nginx > gwapi.yaml

# From the whole cluster
ingress2gateway print --providers=ingress-nginx --all-namespaces > gwapi.yaml
Write the output to a file, review it, and apply it only after the checks below.

The printed output also includes warnings. Treat each one as a to-do item. Four come up often enough to check for every migration.

  • Untranslatable settings. The tool cannot translate every annotation. For example, the configuration-snippet annotation has no direct equivalent. The warning tells you to check the documentation of your Gateway API implementation for an equivalent behaviour.
  • Regex matching. ingress-nginx regex paths are case-insensitive prefix matches. The translated output reflects that with a leading (?i) and a trailing .*. Most teams want exact, case-sensitive matching, so remove both unless you need the old behaviour.
  • Timeouts. The proxy-send and proxy-read timeout annotations are translated to a request timeout as a best-effort default. In the 1.0 announcement, the example becomes 10 seconds. Set the value your service actually needs.
  • URL normalisation. Gateway API does not configure RFC 3986 section 6 URL normalisation. Check whether your application depends on it.

The 1.0 release also has emitter flags for implementation-specific output. The release names envoy-gateway among them, so a conversion aimed at Envoy Gateway can include the extensions that the default output leaves out:

shell
# Add implementation-specific output for Envoy Gateway
ingress2gateway print --providers=ingress-nginx --emitter envoy-gateway --input-file my-manifest.yaml > gwapi.yaml

Gateway API vs Ingress: the same app, both ways

The main difference between Ingress and Gateway API is the split of roles. An Ingress puts the entry point and the routing rules in one object, with controller-specific annotations for anything extra. Gateway API separates them: a Gateway describes the entry point and its listeners, and route objects such as HTTPRoute describe where requests go.

The Gateway API migration guide converts one Ingress with two hosts into resources in two steps. The first step is the Gateway. It names the GatewayClass that the installed implementation provides, creates an HTTP and an HTTPS listener, and points the HTTPS listener at the TLS secret the Ingress already used. The example is verbatim from the guide, with the hostnames unchanged:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: prod
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    hostname: "*.example.com"
  - name: https
    port: 443
    protocol: HTTPS
    hostname: "*.example.com"
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: example-com
The Gateway: one per cluster or per team, owned by the platform side.

The second step is one HTTPRoute per hostname. Each route attaches to the HTTPS listener through parentRefs, and it carries the same path rules the Ingress had. The guide splits one Ingress into a route for foo.example.com and a route for bar.example.com. This is the shorter of the two routes:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: foo
spec:
  parentRefs:
  - name: example-gateway
    sectionName: https
  hostnames:
  - foo.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: foo-app
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /orders
    backendRefs:
    - name: foo-orders-app
      port: 80
The foo.example.com route. The /orders prefix still goes to its own backend.

Check the output against the behaviour you tested before you apply it. The two most common surprises are the case sensitivity of path matches and the default timeouts, both covered above.

Run both controllers and shift traffic

A safe cutover keeps both controllers running at the same time, so the old path stays available while you move traffic. The pattern is the same whether you use a managed platform or do it yourself: install the new controller, copy the routes, send a small share of traffic to the new path, compare error rates and latency, increase the share, switch DNS when the new path carries all traffic, and keep the old controller for a soak period before you remove it.

SeaGit NGINX to Envoy Gateway migration, step by stepEight steps in order: prepare, then set weight 10, 25, 50 and 100 to move traffic to Envoy, then cutover switches DNS, then a soak period of up to seven days, then finish. During the soak, revert DNS can switch traffic back to nginx.1. prepareroutes copied2. set weight 10Envoy gets 10%3. set weight 25Envoy gets 25%4. set weight 50Envoy gets 50%5. set weight 100all on Envoy6. cutoverDNS to Envoy7. soakup to 7 days8. finishapps own routesBefore step 1, 100 percent of traffic stays on nginx. During soak, revert DNS switches traffic back to nginx. Each step is a separate API call.
The SeaGit migration steps, in the order the API allows them. The API accepts weights of 0, 10, 25, 50 and 100; 0 sends all traffic back to NGINX. Cutover needs 100 percent. The soak period can last up to seven days.

The most common failure at the traffic step is not a routing error. It is a behaviour that changed without anyone noticing, such as an auth check, a body size limit or a rate limit. Compare those behaviours on the new path before you move the next share of traffic, and keep the old path ready until the soak period ends.

The cost of waiting

Nothing in the cluster forces the change on the retirement date. That is the risk. The timing of the next vulnerability is not known in advance, so it could come soon or much later. Planning the migration before that happens turns it into a scheduled cutover rather than an incident response, and it lets the team test the new routes against real traffic first.

The migration also touches every Service behind the controller at once, which makes it a poor fit for a single weekend. Start with the cluster that has the fewest Ingress resources, and use that run to learn which annotations your teams depend on.

How SeaGit handles this

SeaGit clusters can run either controller. Here is what the product does today, with the limits stated.

  • New clusters. The Ingress field in the create form offers NGINX Ingress Controller by default, and Gateway API (Envoy Gateway) on Kubernetes 1.33 and later. The Gateway option is available on AWS only.
  • Existing clusters. An AWS cluster that runs NGINX can migrate in place. The migration runs as steps: prepare installs Envoy Gateway and copies your Ingress routes, set weight sends 10, 25, 50 or 100 percent of traffic to Envoy, cutover switches DNS, soak holds the old path for up to seven days, and finish stops the route sync.
  • Deploys during the migration. While the migration is running, each application deploy or removal syncs that application’s Ingress to Envoy automatically, without changing its traffic weight. After finish, each application applies its own HTTPRoutes.
  • Blockers. Some ingress-nginx annotations do not have an Envoy Gateway equivalent. The status response lists them as blockers, and cutover is refused until they are removed or replaced. The documented examples are auth-url, proxy-body-size, rate-limit and auth-type.
  • Limits. The migration API works on AWS EKS clusters on SeaGit only. Azure and GCP are not supported. The cluster must be started for a step to run, and each step is a separate call. Your team still owns the blocker fixes and the cutover decision.

The step-by-step API is documented in the ingress migration guide. The example below shows the first two calls, with the API key header left out, which the guide describes:

shell
# Start the migration (prepare installs Envoy Gateway and copies routes)
curl -X POST "https://seagit.com/api/org/$ORG_ID/clusters/$CLUSTER_ID/ingress-migration?accid=$ACCOUNT_ID" \
  -H "Authorization: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"prepare"}'

# Send 10 percent of traffic to Envoy (weights: 0, 10, 25, 50, 100)
curl -X POST "https://seagit.com/api/org/$ORG_ID/clusters/$CLUSTER_ID/ingress-migration?accid=$ACCOUNT_ID" \
  -H "Authorization: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"set_weight","weight":10}'

If you run clusters somewhere else, or you use another provider, the sections above are the path. SeaGit is one option among several, and the checks on this page apply to any controller you choose. For the cluster model behind this, see bring your own cloud.

Frequently asked questions

Ingress NGINX end of life: FAQ

When does ingress-nginx stop being maintained?

Kubernetes announced the retirement on 11 November 2025. Best-effort maintenance ran until March 2026. After that, according to the project README, there are no further releases, no bugfixes, and no updates to resolve security vulnerabilities. The repository was archived and set to read-only on 24 March 2026.

Will my cluster stop routing traffic when ingress-nginx retires?

No. Existing deployments keep working, and the Helm charts and container images stay published. What ends is maintenance. A vulnerability found in the controller after the cutoff will not get a patch, so the risk grows with time rather than appearing on a single date.

What does Kubernetes recommend instead?

Kubernetes recommends migrating to Gateway API, the successor to the Ingress resource. If you must stay on the Ingress API, the Kubernetes documentation lists other maintained Ingress controllers. Kubernetes says none of the alternatives is a direct drop-in replacement, so the move needs planning and engineering time.

Is there a tool that converts Ingress to Gateway API?

Yes. Ingress2Gateway reached 1.0 on 20 March 2026. Run ingress2gateway print with --providers=ingress-nginx to translate Ingress manifests or a live cluster into Gateway API resources. It warns about settings it cannot translate, so read the warnings before you apply the output.

How do I know whether my cluster depends on ingress-nginx?

Run kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx. That finds a standard install. Then list your IngressClasses and Ingress resources to see which routes depend on it. A controller installed under different labels will not match the first command, so check the IngressClass controller field too.

Does SeaGit migrate ingress for me?

SeaGit offers a Gateway API option based on Envoy Gateway for clusters on Amazon EKS in your own AWS account, and an existing NGINX cluster can move to it in steps. The migration is not automatic. Your team starts each step, and the cluster must not have unresolved Ingress blockers before cutover. Azure and GCP are not supported.

Sources

Checked 10 October 2026.

  1. Ingress NGINX Retirement: What You Need to Know (Kubernetes blog, 11 November 2025) — the original announcement and the March 2026 cutoff
  2. Ingress NGINX: Statement from the Kubernetes Steering and Security Response Committees (29 January 2026) — the 50 percent figure, the kubectl check and the security warning
  3. kubernetes/ingress-nginx (GitHub repository) — archived and read-only since 24 March 2026; the retirement notice in the README
  4. Migrating from Ingress (Gateway API documentation) — Gateway and HTTPRoute mapping and the example conversion
  5. A Welcome Guide for Ingress-NGINX Users (Gateway API documentation) — which role owns the Gateway and which owns the HTTPRoute
  6. Announcing Ingress2Gateway 1.0 (Kubernetes blog, 20 March 2026) — install and print commands, annotation coverage and translation warnings

Read next