# 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](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:

* `GatewayClass`
    
* `Gateway`
    
* `HTTPRoute`, `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)
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763109699462/7aa533e9-2076-4500-84bb-8afa47cedd09.png align="center")
    
* 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**

```yaml
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

```yaml
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](https://github.com/kubernetes-sigs/ingress2gateway)

To install it, just run this command

```yaml
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.

```yaml
#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.

```yaml
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 running
    
* An 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763116169664/f9190d94-1f1f-410a-990e-4cf17d6fcb71.png align="center")

# **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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1763111056349/888ba520-d7c2-4ccf-a10e-8f6b381d8cae.png align="center")

### **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 `/` matchers
    
* TLS secret validity
    
* Route-to-listener alignment
