Migrating from Ingress NGINX to Gateway API : something to be afraid of ?

The CNCF has recently announced a major transition: Ingress Nginx, the “de facto” considered ingress controller, will officially reach end-of-life in March 2026.
Official source here : https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/#:~:text=In%20March%202026%2C%20Ingress%20NGINX,and%20left%20available%20for%20reference.
Of course, it will still be possible to use it but it will no longer receive maintenance, patches or security updates.
The reason are quite simple. The traditional Ingress API:
Only exposes hostnames, paths, and backends
Does not handle advanced TLS scenarios
Does not offer traffic policies or advanced L7 behavior
Cannot support multi-tenant or complex routing without an overload of annotations
Annotations (while powerful) became difficult to maintain, inconsistent, and controller specific.
So, what should we do to avoid CVEs and upcoming troubles ? The answer is quite clear : use a new standard, Gateway API.
In fact, that’s not a new concept : the Gateway API appeared in 2020 and a lot of controllers appeared from 2021 (Envoy, Istio, Nginx via their own provider, Cilium) and started to be massively used since 2022.
Gateway API is a new architecture, not a drop-in replacement
This new approach introduces a richer and more structured model with objects such as:
GatewayClassGatewayHTTPRoute,TCPRoute,GRPCRoute, etc.
This brings far better separation of concerns compared to Ingress.
New features to implement more advanced traffic control
Gateway API supports various features such as:
Header matching
Weighted routing (example here)

Traffic splitting (for blue / green or canary)
Multi-listener setups
Better TLS configuration
Multi-protocol routing
With Ingress, enabling the same functionalites usually required multiple and controller-specific annotations.
Support for diverse traffic types
Gateway API handles not only HTTP, but also:
gRPC
TCP
TLS passthrough
UDP (via implementations that support it)
Ingress is fundamentally HTTP-centric and lacks this protocol diversity.
A practical, real-world migration path: Ingress NGINX to Envoy Gateway
So, now, let’s switch to the best part: a practical (and rollback friendly) way to go from Ingress Nginx to Envoy Gateway. We choose Envoy for this example but we could have chosen Istio, Kong, Traefik, and many more.
Step 1 — Install Envoy Gateway
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.6.0 \
-n envoy-gateway-system \
--create-namespace
We choose to use Envoy Gateway to become the control-plane responsible for processing Gateway API.
It’s quite simple to install with the helm install command.
Step 2 — Create a GatewayClass
We now have to create an GatewayClass
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: gatewayclass1
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
This ensures that all converted resources referencing gatewayClassName: gatewayclass1 are correctly handled by Envoy Gateway.
Step 3 — convert your objects with ingress2gateway
This tool scans existing Ingress objects and generates Gateway API equivalents:
Gateway
HTTPRoute
BackendRefs
TLS references
Host rules
The project code sources are available here:
https://github.com/kubernetes-sigs/ingress2gateway
To install it, just run this command
go install github.com/kubernetes-sigs/ingress2gateway@latest
Once it’s install, you can run this command to first get the converted resources taken from your ingress related objects and thenmove it to api Gateway objects.
In this example, we took the objects from our "gitlab” namespace.
#first, list
/root/go/bin/ingress2gateway print \
--namespace gitlab \
--providers ingress-nginx
#then, apply
/root/go/bin/ingress2gateway print \
--namespace gitlab \
--providers ingress-nginx \
| kubectl apply -f -
Expected output:
A new Gateway resource in the target namespace
A set of HTTPRoute objects matching the original ingress rules
Listeners and TLS configured under the Gateway
Routing logic now controlled by Envoy Gateway
Final step — Verify IP assignment and Gateway readiness
Run this command to list the gateways and services.
kubectl get gateways -A
kubectl get svc -A | grep envoy
This should confirm that:
The Gateway shows
PROGRAMMED=True, meaning that the gateway is ready and runningAn IP is assigned via LoadBalancer (or any IP provider, for example MetalLB)
Routes are attached to the appropriate listeners
DNS or host file entries point to the correct address
Warning: This will not simply “move” your existing Ingress resources into the Gateway API. As explained earlier, this process is reversible because we are not deleting the existing Ingress objects. This means that the new LoadBalancer services created by the Gateway will receive new IP addresses and will not replace the existing Ingress LoadBalancers!
If you need to keep the same IP address, just edit que “loadBalancerIP” parameter in your LoadBalancer services. If you’re using Metallb, don’t forget to restart your controller pod in order to avoid IP attribution errors.

Lessons learned during the migration
Here’re different aspect to care about when preparing your gateway API adoption.
Gateway listeners must include all hostnames
If the Gateway does not list a hostname, Envoy will reject the traffic, even if the HTTPRoute is valid.
TLS is configured on the Gateway, not the HTTPRoute
This is a key difference from Ingress and a common migration pitfall. Here’s the example we used to reference our secret.

Namespace scoping matters
allowedRoutes: Same restricts routes to the Gateway’s namespace.
Incorrect scoping causes silent routing failures.
Ingress2gateway accelerates the process but a manual review is still required
Of course, this tool is very useful and helps to gain a lot of time when enabling gateway APIs, but don’t trust it too much. You need to check all your objects with validations including:
Correct backends
Correct hostnames
Presence of path
/matchersTLS secret validity
Route-to-listener alignment





