Skip to content
Cloud Security DeskSearch
Menu

Technical guideWorkload security

Plan the move from ingress-nginx to Gateway API

Ingress-nginx is archived and will not be patched again. Contain the old controller, inventory the behaviors your clients depend on, and cut over to Gateway API with evidence and a rollback path.

Published
Sources checked
Next review
Reading time
17 minutes
Coverage
Kubernetes · Google Cloud · AWS · Azure
A worn single gate passes one wide lane of traffic to a newer gateway that splits it into four separate route lanes, each ending at its own service.
Conceptual illustration of the move from one annotated ingress controller to explicit Gateway API listeners and routes.

A migration guide for platform and security owners still running ingress-nginx, built from Kubernetes advisories, the official CVE feed, Gateway API and ingress2gateway documentation, and GKE, AWS and Azure controller docs. It gives an inventory method, a behavior mapping matrix, a staged cutover with rollback triggers and the security controls to rebuild.

At a glance

Key findings

  • Ingress-nginx received its last releases on March 19, 2026; its repository is archived and newly found vulnerabilities will not be fixed upstream. [1][10]
  • Twelve ingress-nginx CVEs were published between March 2025 and March 2026, and nine scored 8.8 or higher; most were injection flaws that could expose every Secret a default controller can read. [3][7][8]
  • A faithful YAML conversion can still cause outages, because ingress-nginx regex, rewrite, redirect and normalization defaults do not exist in conformant Gateway API implementations. [4]
  • ingress2gateway 1.0 and later translate more than 30 ingress-nginx annotations and report what they cannot translate, which makes the notifications as important as the output. [5][6]
  • Vendor-packaged controllers have their own deadlines; Microsoft's critical patch support for the AKS application routing NGINX add-on runs through November 2026. [21]

What retirement means for a running cluster

Ingress-nginx stopped being maintained in March 2026. Kubernetes SIG Network and the Security Response Committee announced that after March there would be no further releases, no bug fixes and no updates for newly discovered vulnerabilities, while existing deployments would keep working and the Helm charts and container images would stay available [1]. The GitHub repository is now archived and read-only. Its last controller releases, v1.15.1, v1.14.5 and v1.13.9, were published on March 19, 2026 [10]. A cluster still running it has an internet-facing proxy whose next vulnerability will not be fixed upstream.

The work splits into two tracks that run at the same time. The first reduces exposure on the controller you already have: run the final patched release for your minor line, keep snippet annotations disabled, limit who can create Ingress objects and limit what can reach the admission webhook. The second moves routing behavior, not merely objects, to a Gateway API implementation, or to another maintained Ingress controller where Gateway API is not yet possible. The Kubernetes Steering and Security Response Committees were blunt that none of the alternatives is a drop-in replacement and that the move needs planning and engineering time [2].

Nothing breaks on its own, and that is the hazard. The committees warned that because existing deployments keep working, a team that does not check may not learn it is affected until it is compromised [2]. Their statement also cited internal Datadog research estimating that about half of cloud native environments relied on the controller [2]. The project's own check, run with cluster administrator permissions, is kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx [1]. An empty result is a starting point rather than proof, because repackaged controllers and custom installs can carry other labels.

Vendor packaging moves the deadline, not the destination. The AKS application routing add-on runs a managed controller based on ingress-nginx, and Microsoft says it will provide critical security patches for the add-on's NGINX resources through November 2026 while recommending a move to Gateway API [21]. If a platform vendor ships ingress-nginx to you, find its written end date for security fixes and treat that date as your real cutover deadline.

The rest of this guide treats the migration as an inventory of behaviors. Ingress-nginx accumulated defaults that most teams never chose, such as case-insensitive prefix regex, implicit redirects and URL normalization, and a faithful conversion of the YAML will drop them without warning [4]. The sections that follow cover why the risk changed, how to build the inventory, how to map each behavior, how to choose a replacement, how to cut over with rollback criteria, which security controls to rebuild and what evidence to keep. All sources were reviewed on October 7, 2026.

How the 2025 and 2026 advisories changed the risk

On March 24, 2025, the maintainers shipped v1.12.1 and v1.11.5 to fix five vulnerabilities [3]. Four of them were flaws in how the controller turned Ingress fields into nginx configuration. A crafted Ingress could make nginx reveal Secrets the controller could read, and in a default installation the controller can read every Secret in the cluster [3]. That default is what turned a templating bug into a cluster-wide credential problem.

CVE-2025-1974, scored 9.8, removed the need to create an Ingress at all. It let anything on the Pod network reach the injection paths through the controller's validating admission webhook, with no credentials [3]. Wiz, whose researchers reported the issues, explained that the webhook validated candidate configuration by running the nginx configuration test, and that the undocumented ssl_engine directive could load a shared library during that test [9]. Wiz estimated that about 43 percent of cloud environments were vulnerable and reported more than 6,500 clusters exposing the admission endpoint to the public internet [9]. The interim mitigation in the Kubernetes advisory was to disable the webhook, either with the Helm value controller.admissionWebhooks.enabled=false or by deleting the ingress-nginx-admission ValidatingWebhookConfiguration and removing --validating-webhook from the controller arguments [3].

The pattern continued into the final months of maintenance. The official Kubernetes CVE feed lists seven more ingress-nginx advisories between February 2 and March 19, 2026: configuration injection through auth-method, through the rules.http.paths.path field, through auth-proxy-set-headers, through rewrite-target and through a comment-based combination of annotations, plus a denial of service in the admission controller and an auth-url protection bypass that occurs only with a defective custom error backend [7]. The chart shows the CVSS 3.1 base scores the Kubernetes CNA assigned to all twelve, as displayed by NVD [8]. Nine of the twelve are 8.8 or higher.

The repeated 8.8 vector reads as network reachable, low complexity and low privileges required. The interpretation here is that this is what happens when a namespace-scoped write, creating or editing an Ingress, reaches a process that holds cluster-wide read access to Secrets. The retirement notice makes the same point: options once considered helpful, such as arbitrary nginx directives through snippet annotations, have come to be considered serious security flaws [1].

As of this review the feed lists no ingress-nginx advisory after March 19, 2026 [7]. With the repository archived, that silence is what you would expect from a project with nobody to receive, fix and announce reports. It is not evidence that the code is clean. Plan as though the next flaw will be published before any fix exists, and keep the compensating controls in the security section in place until the controller is gone.

Figure 01

Twelve ingress-nginx advisories in twelve months

Nine of the twelve advisories from March 2025 to March 2026 scored 8.8 or higher. [7][8]

Horizontal bar chart of CVSS 3.1 base scores for twelve ingress-nginx CVEs. CVE-2025-1974 scores 9.8, eight others score 8.8, CVE-2026-24514 scores 6.5, CVE-2025-24513 scores 4.8 and CVE-2026-24513 scores 3.1.

Source. Official Kubernetes CVE feed for the list and dates; NVD CVE records for the CVSS 3.1 base scores assigned by the Kubernetes CNA. [7][8]

Method. Every ingress-nginx entry in the official feed dated March 2025 through March 2026, ordered by feed date. Scores are copied without change from each NVD record's CNA metric, checked on October 7, 2026. Feed dates are the date the Kubernetes issue was opened.

Accessible table and figure data
Figure 1 accessible table
CVE and weaknessFeed dateCVSS 3.1 base score
CVE-2025-24513 auth secret path traversal2025-03-234.8
CVE-2025-24514 auth-url injection2025-03-238.8
CVE-2025-1097 auth-tls-match-cn injection2025-03-238.8
CVE-2025-1098 mirror annotation injection2025-03-238.8
CVE-2025-1974 admission webhook RCE2025-03-239.8
CVE-2026-1580 auth-method injection2026-02-028.8
CVE-2026-24512 path field injection2026-02-028.8
CVE-2026-24513 auth-url bypass2026-02-023.1
CVE-2026-24514 admission denial of service2026-02-026.5
CVE-2025-15566 auth-proxy-set-headers injection2026-02-068.8
CVE-2026-3288 rewrite-target injection2026-03-098.8
CVE-2026-4342 comment-based injection2026-03-198.8
Figure 1 accessible table
CVE and weaknessFeed dateCVSS 3.1 base score
CVE-2025-24513 auth secret path traversal2025-03-234.8
CVE-2025-24514 auth-url injection2025-03-238.8
CVE-2025-1097 auth-tls-match-cn injection2025-03-238.8
CVE-2025-1098 mirror annotation injection2025-03-238.8
CVE-2025-1974 admission webhook RCE2025-03-239.8
CVE-2026-1580 auth-method injection2026-02-028.8
CVE-2026-24512 path field injection2026-02-028.8
CVE-2026-24513 auth-url bypass2026-02-023.1
CVE-2026-24514 admission denial of service2026-02-026.5
CVE-2025-15566 auth-proxy-set-headers injection2026-02-068.8
CVE-2026-3288 rewrite-target injection2026-03-098.8
CVE-2026-4342 comment-based injection2026-03-198.8

Inventory controllers, classes and annotations

Start with controllers rather than Ingress objects. The label selector from the advisories finds standard installs, but the inventory should also list every IngressClass and its spec.controller value. The default ingress-nginx controller class is k8s.io/ingress-nginx; Ingresses that name no class are served only if the controller runs with --watch-ingress-without-class=true or its IngressClass is the cluster default [13]. Check both settings, because classless Ingresses served that way are the ones most often missed, and they stop routing the moment the controller is removed.

Next, list every Ingress with its class and its nginx.ingress.kubernetes.io/ annotations, then count how often each annotation appears. The fragment below does both with kubectl and jq. Its output seeds the behavior register: one row per distinct annotation and value pattern, each with an owning team, the hosts it affects and a decision to translate it, replace it with an implementation policy or drop it.

The ingress-nginx documentation assigns every annotation a risk level. Counting its table gives 136 annotations: 5 Critical, 10 High, 45 Medium and 76 Low [11]. The Critical five are the snippet annotations, configuration-snippet, server-snippet, auth-snippet, modsecurity-snippet and stream-snippet. The High group contains auth-url, auth-tls-match-cn, mirror-host and mirror-target, the same annotations named in three of the March 2025 advisories [7][11]. Sort the register by that risk level so review time goes first to the places where injection has already happened.

Read the controller ConfigMap as closely as the Ingresses. allow-snippet-annotations defaults to false and annotations-risk-level defaults to High, so Critical annotations are rejected unless someone loosened the setting [12]. If either value was changed, the register must name every team that depends on snippets, because a snippet is raw nginx configuration with no Gateway API equivalent. Global ConfigMap keys such as timeouts, custom headers and strict-validate-path-type change behavior for every Ingress the controller serves and need rows of their own.

Capture traffic as well as configuration. For each host, record the paths clients actually request over a representative window, including requests that currently receive a redirect. The existing controller's access logs are the cheapest source. That list becomes the replay set for acceptance testing, and it is the only reliable way to learn that clients depend on a behavior nobody configured on purpose, such as an automatic trailing-slash redirect.

Example inventory fragment. Needs read access to pods, IngressClasses and Ingresses across namespaces, plus jq. Controllers installed under other labels will not appear in the first command.
# 1. Controller pods, using the selector from the Kubernetes advisories
kubectl get pods --all-namespaces \
  --selector app.kubernetes.io/name=ingress-nginx -o wide

# 2. Every IngressClass and the controller that claims it
kubectl get ingressclass \
  -o custom-columns=NAME:.metadata.name,CONTROLLER:.spec.controller

# 3. Every Ingress with its class (or none) and its ingress-nginx annotations
kubectl get ingress --all-namespaces -o json | jq -r '
  .items[]
  | [ .metadata.namespace,
      .metadata.name,
      (.spec.ingressClassName // "none"),
      ([ .metadata.annotations // {} | keys[]
         | select(startswith("nginx.ingress.kubernetes.io/")) ] | join(",")) ]
  | @tsv'

# 4. How often each ingress-nginx annotation is used across the cluster
kubectl get ingress --all-namespaces -o json | jq -r '
  .items[].metadata.annotations // {} | keys[]
  | select(startswith("nginx.ingress.kubernetes.io/"))' \
  | sort | uniq -c | sort -rn
Figure 02

Where ingress-nginx annotation risk concentrates

Only 15 of 136 documented annotations are rated High or Critical, and they include every annotation named in the March 2025 injection advisories. [11]

Bar chart counting ingress-nginx annotations by documented risk level: 5 Critical, 10 High, 45 Medium and 76 Low, out of 136.

Source. Annotations Scope and Risk table in the ingress-nginx documentation. [11]

Method. Rows of the table counted by the value in its Risk column on October 7, 2026; 136 rows in total, each a distinct annotation. Risk levels are the project's own ratings, not an independent assessment.

Accessible table and figure data
Figure 2 accessible table
Documented risk levelAnnotations
Critical5
High10
Medium45
Low76
Figure 2 accessible table
Documented risk levelAnnotations
Critical5
High10
Medium45
Low76

Map behaviors to Gateway API resources

Gateway API separates what Ingress combined. A Gateway declares listeners with an address, port, protocol, hostname and TLS settings. An HTTPRoute attaches to a Gateway through parentRefs and holds matches, filters and backends [14]. The core Ingress fields translate directly: host becomes an HTTPRoute hostname, Prefix becomes PathPrefix, Exact stays Exact and the TLS section becomes a listener's certificateRefs. Two differences catch people. Gateway API has no default backend, so you write an explicit rule for the / prefix, and it prescribes how rules merge and which match wins, where Ingress left both to each controller [14].

Annotations are where outages hide. Kubernetes documented five ingress-nginx behaviors that a careful translation can still break [4]. With use-regex, patterns are case-insensitive prefix matches, so /[A-Z]{3} also matches /uuid. Regex mode then applies to every path on that host across all Ingresses, not only the annotated one. rewrite-target switches on regex mode by itself. A request missing a trailing slash gets a 301 to the slashed path. And URLs are normalized before matching, so /ip/abc/../../uuid reaches the rule for /uuid [4].

The regex, rewrite and redirect behaviors can each turn a working request into a 404 on a conformant Gateway API implementation, because conformant implementations do not add redirects on their own and do not reinterpret Exact or PathPrefix matches as regex [4]. Normalization is less predictable: Istio, Envoy Gateway and kgateway remove . and .. segments by default, but the details vary and cannot be set through the standard API [4][5]. RegularExpression path matching is implementation-specific, and the Envoy-based implementations named in that post, Istio, Envoy Gateway and kgateway, perform full, case-sensitive matches [4][14]. Decide behavior by behavior whether to preserve a quirk, for example with a leading (?i) and a trailing .*, or to fix it, and write the decision into the register.

ingress2gateway is the tool the project points to. Version 1.0, released on March 20, 2026, expanded ingress-nginx support from three annotations to more than 30, backed by integration tests that run ingress-nginx and Gateway API controllers in live clusters and compare their runtime behavior [5]. The latest release at review time is v1.2.0 from July 7, 2026 [6]. Its print command reads from a cluster or from manifest files, writes YAML, JSON or KYAML, and reports what it could not translate. The --emitter flag adds implementation-specific resources for agentgateway, Airlock Microgateway, Envoy Gateway, GKE or kgateway [6].

Treat the notifications as half of the output. In the project's walkthrough, configuration-snippet is reported as unsupported, the timeout annotations get a best-effort translation because ingress-nginx timeouts are TCP-level while timeouts.request covers a whole request, proxy-body-size has no standard equivalent, URL normalization cannot be set through the standard API, and the tool adds a port 80 listener with a 308 redirect to match the ingress-nginx default [5]. Some annotations are recognized but deliberately left unconverted, including canary-by-cookie, canary-by-header-pattern, custom-headers and proxy-ssl-protocols, and a rewrite-target that uses capture groups such as $1 is flagged instead of translated [6].

The matrix is a starting map, not a conversion table. Rows that land on an implementation policy have no portable field: authentication, source IP allowlists and body size limits live in each implementation's own extension resources, and the Gateway API migration guide says features such as authentication need those extensions [14]. HTTP external authentication entered the Experimental channel for HTTPRoute in Gateway API v1.4, was extended to the Gateway level in v1.5, and is still not in Standard [16].

The HTTPRoute fragment below shows two quirks preserved on purpose after review: the trailing-slash redirect is written as an explicit RequestRedirect rule, and a former use-regex path keeps its case-insensitive prefix behavior. Whether (?i) works depends on the implementation's regex engine, so treat that line as something to confirm rather than a portable pattern.

Example HTTPRoute fragment for Gateway API v1 with placeholder names. RegularExpression semantics are implementation-specific; confirm (?i) support before relying on it.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop-orders
  namespace: shop
spec:
  parentRefs:
  - name: shared-edge
    namespace: edge
    sectionName: https-shop
  hostnames:
  - shop.example.com
  rules:
  # Preserve the implicit ingress-nginx trailing-slash redirect on purpose
  - matches:
    - path:
        type: Exact
        value: /orders
    filters:
    - type: RequestRedirect
      requestRedirect:
        statusCode: 301
        path:
          type: ReplaceFullPath
          replaceFullPath: /orders/
  - matches:
    - path:
        type: PathPrefix
        value: /orders/
    backendRefs:
    - name: orders
      port: 8080
  # Former use-regex path, kept case-insensitive and prefix-style after review
  - matches:
    - path:
        type: RegularExpression
        value: "(?i)/api/v[0-9]+/.*"
    backendRefs:
    - name: api
      port: 8080
Figure 03

Map behaviors, not annotations

Several common behaviors have no portable Gateway API field and need an explicit decision. [4][5][6][14]

Matrix of ten ingress-nginx behaviors, the Gateway API resource or setting each maps to, and the gap to resolve before cutover.

Source. Conceptual mapping compiled from Kubernetes migration guidance, ingress2gateway documentation and Gateway API release notes. [4][5][6][14][16]

Method. Conceptual editorial synthesis of documented behavior; no conversion was run. Gateway API implementations differ, so each row needs confirmation against the chosen implementation.

Accessible table and figure data
Figure 3 accessible table
BehaviorMaps toGap
use-regex pathsRegularExpression path matchPrefix and case-insensitive semantics differ
rewrite-targetURLRewrite filterImplies regex; capture groups flagged
Trailing-slash 301Explicit RequestRedirect ruleNot added by conformant gateways
URL normalizationImplementation defaultNot configurable in the standard API
Canary weight or headerbackendRefs weight, header matchCookie canary not converted
enable-corsCORS filter, Standard since v1.5Review generated defaults
ssl-redirect defaultPort 80 listener, 308 redirectRemove if HTTP is unneeded
proxy timeoutstimeouts.requestTCP timeouts versus whole request
auth-url, source rangesImplementation policyNo portable core field
Snippet annotationsNo equivalentRebuild or drop each directive
Figure 3 accessible table
BehaviorMaps toGap
use-regex pathsRegularExpression path matchPrefix and case-insensitive semantics differ
rewrite-targetURLRewrite filterImplies regex; capture groups flagged
Trailing-slash 301Explicit RequestRedirect ruleNot added by conformant gateways
URL normalizationImplementation defaultNot configurable in the standard API
Canary weight or headerbackendRefs weight, header matchCookie canary not converted
enable-corsCORS filter, Standard since v1.5Review generated defaults
ssl-redirect defaultPort 80 listener, 308 redirectRemove if HTTP is unneeded
proxy timeoutstimeouts.requestTCP timeouts versus whole request
auth-url, source rangesImplementation policyNo portable core field
Snippet annotationsNo equivalentRebuild or drop each directive

Choose a replacement implementation

Gateway API is the direction the Kubernetes project recommends, and the retirement notice also allows a different Ingress controller where Ingress has to stay for now [1]. Do not confuse ingress-nginx with NGINX Ingress Controller, a separate controller developed by F5 [4]. Moving to another Ingress controller is still an annotation translation, and the result stays tied to that vendor, because Ingress annotations are implementation-specific by design [14].

Pick the Gateway API version and channel first. Gateway API publishes a Standard channel with compatibility guarantees and an Experimental channel without them. v1.5.0, released February 27, 2026, moved ListenerSet, TLSRoute v1, the HTTPRoute CORS filter and client certificate validation to Standard, and added a safe-upgrades ValidatingAdmissionPolicy that blocks installing Experimental CRDs over Standard ones and downgrading below 1.5 [16]. v1.6.0 followed on June 29, 2026 and promoted TCPRoute and UDPRoute to Standard, and v1.6.3 shipped on October 6, 2026 [16]. Stay on Standard unless a register row needs an Experimental field, and record who owns the CRDs, since a managed platform may install them for you.

Use conformance results to filter candidates. The Gateway API site publishes per-version tables built from conformance reports that implementations submit; an implementation appears only if it passes Core conformance for that resource, and reports with skipped tests are excluded [17]. The v1.6 HTTPRoute table tracks 38 extended features, including CORS, path rewrite, request mirroring and request timeouts [17]. Map each register row to one of those features. A feature marked as not supported means it was not claimed in that implementation's report for that version, which is not the same as proven absent, so confirm with the vendor's documentation.

The managed controllers trade portability for a data plane someone else operates, and each has limits that reshape the mapping. GKE's Gateway controller reconciles Gateway resources into Cloud Load Balancing, offers GatewayClasses such as gke-l7-global-external-managed, gke-l7-regional-external-managed and gke-l7-rilb, and uses policy resources such as HealthCheckPolicy and GCPBackendPolicy for settings outside the core API [18]. The AWS Load Balancer Controller serves HTTPRoute and GRPCRoute with an Application Load Balancer and L4 routes with a Network Load Balancer, does not support mixing L4 and L7 routes on one Gateway, and cannot take TLS certificates from a listener's certificateRefs [19]. Application Gateway for Containers keeps the data plane outside the AKS cluster; its ALB Controller implements Gateway API v1.5 and allows only ports 80 and 443 on Gateway listeners [20].

For an in-cluster implementation, weigh three things beyond the conformance table: whether your team already operates its proxy, whether your CNI or cloud integrates it, and how quickly its project ships security fixes. That last point is the lesson of ingress-nginx. Ask how many maintainers the project has and how its security reports are handled, because the controller you choose will hold the same edge position and may need read access to certificate Secrets.

Multi-team clusters should also check two limits early. A Gateway accepts at most 64 listeners, so a platform that gives every hostname its own listener will hit the cap and needs another delegation mechanism [15]. ListenerSet, Standard since v1.5, lets application teams contribute listeners without editing the shared Gateway, but only if your chosen implementation supports it [5][16].

Replacement options reviewed on October 7, 2026, with the limits most likely to change a migration plan.
OptionWhat runs the data planeCheck before choosing
GKE Gateway controllerCloud Load BalancingGatewayClass choice and GCP policy resources [18]
AWS Load Balancer ControllerALB for L7, NLB for L4No certificateRefs; no mixed L4 and L7 Gateway [19]
Application Gateway for ContainersAzure, outside the clusterGateway API v1.5; listener ports 80 and 443 [20]
AKS application routing NGINXManaged ingress-nginxCritical patches only through November 2026 [21]
In-cluster implementationYour nodesConformance report for your Gateway API version [17]
Figure 04

What changes at the edge

Gateway API moves edge decisions from one annotated object into owned, typed resources. [3][10][14][15]

Before-and-after comparison of seven edge concerns, from entry points and route ownership to controller credentials, under ingress-nginx and under Gateway API.

Source. Conceptual comparison based on the ingress-nginx advisory and README and on Gateway API migration and security documentation. [3][10][14][15]

Method. Conceptual summary of documented defaults and design; specific implementations can differ, and no deployment was inspected.

Accessible table and figure data
Figure 4 accessible table
AspectBeforeAfter
Entry pointImplicit HTTP and HTTPS per controllerExplicit listeners on an owned Gateway
Route authorityAny user who can create an IngressRoutes attach where listeners allow
Hostname claimsMerged by the controllerDelegated per listener; older route wins
Cross-namespace useNot part of the Ingress specReferenceGrant in the target namespace
ExtensionsAnnotations and nginx snippetsTyped filters and implementation policies
Controller secretsAll Secrets readable by defaultImplementation RBAC to review
Trust modelIngress writers treated as adminsRole-based write access per resource
Figure 4 accessible table
AspectBeforeAfter
Entry pointImplicit HTTP and HTTPS per controllerExplicit listeners on an owned Gateway
Route authorityAny user who can create an IngressRoutes attach where listeners allow
Hostname claimsMerged by the controllerDelegated per listener; older route wins
Cross-namespace useNot part of the Ingress specReferenceGrant in the target namespace
ExtensionsAnnotations and nginx snippetsTyped filters and implementation policies
Controller secretsAll Secrets readable by defaultImplementation RBAC to review
Trust modelIngress writers treated as adminsRole-based write access per resource

Run both paths, then shift traffic by weight

A Gateway API implementation can run beside ingress-nginx with its own address, so the new path can be built and tested while production traffic stays where it is. The ingress2gateway maintainers recommend exactly that sequence: validate in a development cluster, deploy the Gateway API configuration alongside the existing Ingress, then shift traffic gradually with weighted DNS, a cloud load balancer or the platform's traffic-splitting features, and remove the Ingress resources and controller only at the end [5].

The flowchart breaks that sequence into seven stages, each with the evidence that lets you leave it and the signal that sends you back. The first stage, freezing the inventory, is the one teams skip. During the migration window, any new nginx.ingress.kubernetes.io/ annotation is a behavior the register does not know about. A review gate, or an admission policy that rejects unreviewed annotations, keeps the register and the cluster in step.

Translate one namespace or one host group at a time, and commit the ingress2gateway output together with its notifications. A reviewer should see every warning and the decision taken on it. When the parallel Gateway is up, read status before sending a single request: each Gateway should report Accepted and each route should be accepted with its references resolved, because a route that never attached will produce 404s that look like routing bugs [16].

Replay the captured request set against both addresses and compare status codes, Location headers on redirects and the backend that answered. Differences are the deliverable of this stage, not a failure: each one is either a quirk the register should preserve or a bug the register should record as fixed. Then shift traffic by weight. DNS weights are subject to resolver caching, so the effective split and the speed of a rollback both lag behind the record you changed.

Write rollback criteria before the first weight change. As a hypothetical starting set: any rise in 404 or 5xx responses on a migrated host above its recorded baseline, any redirect that differs from the replay result, and any authentication or allowlist test that passes on the new path when it should fail. Rolling back means returning the weight to the old path, which only works if that path is untouched. Freeze changes to the old Ingress objects during the hold window and keep their certificates renewing.

Watch the integrations that read Ingress objects. The Gateway API migration guide names cert-manager and ExternalDNS as projects that integrate with the Ingress API [14]. Confirm each one is configured for the new Gateway and route resources before cutover. Otherwise certificate issuance or DNS record management can keep following the old objects until the day they are deleted.

Figure 05

A staged cutover with exits and rollbacks

Each stage needs exit evidence and a predefined signal that returns traffic to the old path. [5]

Flowchart of seven migration stages from freezing the inventory to decommissioning, each with exit evidence and a rollback trigger.

Source. Conceptual plan built on the ingress2gateway maintainers' recommended sequence. [5]

Method. Conceptual staging framework; thresholds are deliberately unspecified and must come from each service's recorded baseline. No cutover was performed.

Accessible table and figure data
Figure 5 accessible table
StageExit evidenceRollback trigger
Freeze the inventoryEvery Ingress, class and annotation ownedUnreviewed annotation appears
Translate and reviewEach notification resolved or acceptedUnexplained warning remains
Deploy parallel GatewayGateway and routes Accepted, refs resolvedRoute fails to attach
Replay real requestsStatus, redirects and backends matchUnexplained 404 or redirect change
Shift traffic by weightError rates within recorded baselineAgreed threshold breached
Hold both pathsOld path idle but intactIncident needs the old path
DecommissionController, webhook and Secret access removedNone; rollback window closed
Figure 5 accessible table
StageExit evidenceRollback trigger
Freeze the inventoryEvery Ingress, class and annotation ownedUnreviewed annotation appears
Translate and reviewEach notification resolved or acceptedUnexplained warning remains
Deploy parallel GatewayGateway and routes Accepted, refs resolvedRoute fails to attach
Replay real requestsStatus, redirects and backends matchUnexplained 404 or redirect change
Shift traffic by weightError rates within recorded baselineAgreed threshold breached
Hold both pathsOld path idle but intactIncident needs the old path
DecommissionController, webhook and Secret access removedNone; rollback window closed

Security controls to rebuild on the new edge

The archived ingress-nginx README states the trust model plainly: the project assumes that users who can create Ingress objects are cluster administrators, and it warns against multi-tenant production use [10]. Gateway API gives you the chance to retire that assumption. Its security model assigns write access by role: infrastructure providers manage GatewayClasses, cluster operators manage Gateways and application developers manage routes, with an optional application admin tier scoped to specific namespaces [15]. Write the RBAC for those roles before anyone migrates a route.

Delegate listeners explicitly. A listener's allowedRoutes decides which namespaces may attach routes, and the Gateway API security guidance recommends selecting namespaces by the kubernetes.io/metadata.name label, because anyone who can label namespaces could otherwise change which namespaces a Gateway accepts [15]. Hostnames need the same care. When two routes claim the same hostname, conflict resolution usually favors the older resource, so a team that adds a hostname to an older route can take traffic from a newer one. The guidance is to bind hostnames to specific namespaces at the listener and, where listeners run out, to enforce allowed hostnames with a ValidatingAdmissionPolicy [15].

Cross-namespace references need a handshake. A Gateway referencing a Secret in another namespace, or a route referencing a Service elsewhere, requires a ReferenceGrant in the target namespace, and the security guidance suggests restricting which namespaces may create ReferenceGrants at all [15]. Review every grant as a permission, because that is what it is.

Re-examine controller credentials. Ingress-nginx could read every Secret in the cluster by default [3]. Before adopting a replacement, read its ClusterRoles and record which Secrets it can read and why. Managed controllers can shift that boundary: the AWS Load Balancer Controller, for example, does not read TLS certificates from listener certificateRefs at all [19]. Where Secrets are still used, prefer certificates referenced per listener over a controller that can read everything.

Rebuild each annotation-based control as a tested policy. A whitelist-source-range, an auth-url or a ModSecurity rule set does not carry over as a standard field, so each needs an implementation policy or a different layer, such as the WAF that Application Gateway for Containers offers [20]. Test the denied path for every one: a request from outside the allowed range, a request without valid credentials, a request the WAF should block. A route that serves traffic proves nothing about the control that was supposed to refuse it.

Until decommission, keep the old controller contained. Wiz's mitigation guidance was to enforce network policies so that only the Kubernetes API server can reach the admission controller [9]. Confirm that your network plugin actually enforces those policies, and keep the old controller on its last patched release with snippets disabled.

Example Gateway fragment with placeholder names. The listener binds one hostname and accepts HTTPRoutes only from the shop namespace. Some managed controllers, including the AWS Load Balancer Controller, do not use certificateRefs.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-edge
  namespace: edge
spec:
  gatewayClassName: example-gateway-class
  listeners:
  - name: https-shop
    protocol: HTTPS
    port: 443
    hostname: shop.example.com
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: shop-example-com-tls
    allowedRoutes:
      kinds:
      - kind: HTTPRoute
      namespaces:
        from: Selector
        selector:
          matchLabels:
            kubernetes.io/metadata.name: shop

Rollback criteria and acceptance evidence

Accept the migration host by host, not cluster by cluster. A host is done when four things are recorded: every register row for it has a closed decision, every ingress2gateway notification on its objects is resolved or explicitly accepted by a named owner, the replay set shows matching or intentionally changed behavior, and every denied-path test for its controls fails closed on the new path. Anything less is a partial migration with an old controller still in the loop.

Decommission only after the old path has been idle for an agreed window, measured from its own access logs or request metrics rather than inferred from DNS. Then remove the pieces in this order: the Ingress objects, the ingress-nginx-admission ValidatingWebhookConfiguration, the controller Deployment or DaemonSet and its Service, and finally the IngressClass and the ClusterRole bindings that granted Secret access [3]. A webhook configuration left behind after its Service is gone becomes an admission dependency with nothing behind it.

Keep the record. The register, the raw and reviewed ingress2gateway output, the replay comparisons and the denied-path results together explain why the new edge behaves as it does. They are what an incident responder needs when a client reports a changed redirect six months later, and what an auditor needs to see that the retired controller is gone rather than merely unused. Audit logging for changes to Gateways, routes and ReferenceGrants gives that record a continuing tail.

The first week of the migration

If you are starting this week, the order matters more than the tooling. Contain first, inventory second and translate third, because a translation built on an incomplete inventory produces a confident plan for the wrong cluster.

  • Confirm every ingress-nginx instance, including vendor-packaged ones, and find the support end date that actually applies to each.
  • Contain the old controller: final patched release, snippets disabled, annotations-risk-level at its default or stricter, webhook reachable only from the API server.
  • Build the behavior register from IngressClasses, Ingresses, annotations, ConfigMap keys and captured client paths, sorted by documented annotation risk.
  • Choose an implementation and Gateway API channel against the register, using conformance results and vendor limits rather than feature lists.
  • Write RBAC, listener delegation, hostname rules and ReferenceGrant restrictions before migrating the first route.
  • Translate with ingress2gateway one host group at a time, review every notification and record each preserved or fixed quirk.
  • Run both paths, replay real requests, shift by weight against predefined rollback criteria and hold the old path intact until the agreed window ends.
  • Decommission the controller, its webhook configuration and its Secret access, and keep the evidence.

Method and provenance

Source-led technical analysis of Kubernetes project announcements and advisories, the official Kubernetes CVE feed, NVD records, Gateway API and ingress2gateway documentation and release notes, and provider documentation for GKE, AWS and Azure. All sources were reviewed on October 7, 2026.

No cluster, controller or migration was run or inspected. Implementation behavior, conformance results, provider limits and support dates are bounded to the cited documentation as of the review date and are likely to change.

AI assistance. AI assisted research, source verification, drafting and visual production. No personal deployment experience, independent human review or live test is claimed.

Published under the Cloud Security Desk organizational byline. Read the practitioner guide policy.

References

  1. Ingress NGINX Retirement: What You Need to Know Kubernetes. Published . Accessed .
  2. Ingress NGINX: Statement from the Kubernetes Steering and Security Response Committees Kubernetes. Published . Accessed .
  3. Ingress-nginx CVE-2025-1974: What You Need to Know Kubernetes. Published . Accessed .
  4. Before You Migrate: Five Surprising Ingress-NGINX Behaviors You Need to Know Kubernetes. Published . Accessed .
  5. Announcing Ingress2Gateway 1.0: Your Path to Gateway API Kubernetes. Published . Accessed .
  6. Official CVE Feed Kubernetes. Accessed .
  7. CVE-2025-1974 detail and linked ingress-nginx CVE records NIST National Vulnerability Database. Accessed .
  8. Annotations Scope and Risk Kubernetes ingress-nginx project. Accessed .
  9. ingress-nginx ConfigMap options Kubernetes ingress-nginx project. Accessed .
  10. Multiple Ingress controllers Kubernetes ingress-nginx project. Accessed .
  11. Migrating from Ingress Kubernetes Gateway API project. Accessed .
  12. Gateway API security Kubernetes Gateway API project. Accessed .
  13. Gateway API releases, v1.5.0 to v1.6.3 release notes Kubernetes SIG Network. Accessed .
  14. Gateway API v1.6 implementation conformance tables Kubernetes Gateway API project. Accessed .
  15. About the Gateway API in GKE Google Cloud. Accessed .
  16. AWS Load Balancer Controller Gateway API guide Kubernetes SIG AWS. Accessed .
  17. What is Application Gateway for Containers? Microsoft. Accessed .