Migrating from Ingress to Gateway API - The Modern Way to Expose Kubernetes Services


The wake-up call
On November 11 2025, the Kubernetes SIG Network and the Kubernetes Security Response Committee announced that the Ingress-NGINX project will be retired as of March 2026, after that point, no bug fixes or security patches will be issued.
Official announcement on kubernetes.io
If you’re still running Ingress-NGINX in production, you have a ticking clock. That makes migrating to a supported alternative, notably the Gateway API, not just a “nice to do”, but a must.
Why move from Ingress (or Ingress-NGINX) to Gateway API?
While Ingress (and by extension many Ingress controllers) has served well, it’s showing its limits in modern use cases like multi-tenant clusters, advanced routing, TLS offload, service-mesh integration, and multi-protocol support.
The Gateway API addresses these directly, with three core benefits:
- Clear separation of concerns: infra teams define entry points; app teams define routing.
- Extensible, expressive API: supports HTTPRoute, TCPRoute, UDPRoute, filters, etc.
- Standard-based and controller-agnostic: less reliance on vendor-specific annotations.
The building blocks: Ingress vs Gateway API
| Legacy | Gateway API |
|---|---|
Ingress | HTTPRoute / TCPRoute / UDPRoute |
IngressClass | GatewayClass |
| — | Gateway (defines the actual listener/entry point) |
Gateway API hierarchy:
- GatewayClass – Defines the implementation (e.g., “nginx”, “istio”, “traefik”).
- Gateway – Defines how and where traffic enters (ports, hostnames, TLS, etc.).
- HTTPRoute / TCPRoute / UDPRoute – Define how traffic is routed to Kubernetes services.
- BackendRef – Points to a Service or endpoint.
Example: From Ingress to Gateway API
Original Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
Equivalent Gateway API setup
1. GatewayClass (if not already present)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx
spec:
controllerName: k8s.io/nginx
Many controllers come with this pre-installed.
2. Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "example.com"
allowedRoutes:
namespaces:
from: Same
3. HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route
namespace: default
spec:
parentRefs:
- name: my-gateway
namespace: default
hostnames:
- "example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: my-service
port: 80
That covers the same host, path, backend scenario — but now using Gateway API.
Step-by-step migration process
-
Inventory your existing Ingress resources
Identify which controllers you’re using (especially Ingress-NGINX). -
Check controller support
Runkubectl get gatewayclassmost modern controllers support Gateway API. -
Deploy GatewayClass/Gateway
Install the Gateway API CRDs if not already present, and create a shared Gateway. -
Convert one Ingress
Pick a simple Ingress, convert it to an HTTPRoute, and attach it to the Gateway.
Keep the Ingress temporarily to test routing side by side. -
Test and validate
Verify with:kubectl describe gateway my-gateway kubectl get httproute kubectl get svc -
Migrate gradually
Move additional Ingress resources one by one. -
Decommission Ingress-NGINX
Plan to retire it before March 2026 to stay secure. -
Update documentation/runbooks
Ensure teams know routing now goes through the Gateway API.
Field-mapping cheat sheet: Ingress → Gateway API
| Ingress field | Gateway API equivalent |
|---|---|
ingressClassName | gatewayClassName (in Gateway) |
spec.rules[].host | hostnames (in HTTPRoute) |
spec.rules[].http.paths[].path | rules.matches.path.value (in HTTPRoute) |
backend.service.name | backendRefs.name |
backend.service.port.number | backendRefs.port |
| Controller-specific annotations | Use filters or extensions in Gateway API |
Best practices
-
Shared Gateway, multiple routes
Use one Gateway for many routes, isolating apps via namespaces. -
Role separation
Infra owns Gateways; app teams own HTTPRoutes. -
GitOps-friendly setup
Version-control Gateways, Routes, and Services together. -
Avoid controller annotations
Replacenginx.ingress.kubernetes.io/*annotations with first-class Gateway filters. -
Monitor routing status
Use:kubectl describe httproute <name>Look for
AcceptedorNotAcceptedconditions. -
Keep a fallback
During migration, keep the old Ingress until you fully validate Gateway routes.
What happens to your old Ingress (and Ingress-NGINX)?
- Ingress API remains supported in Kubernetes for now.
- Ingress-NGINX controller will receive no updates after March 2026.
- Migrate your configuration to Gateway API or another supported controller.
- After migration, decommission Ingress-NGINX to avoid unpatched components.
Summary
Migrating from Ingress (especially Ingress-NGINX) to the Gateway API is more than rewriting YAML — it’s about upgrading how we manage network traffic in Kubernetes.
- Ingress mindset: “Each app defines its own entry point.”
- Gateway mindset: “Infra manages shared gateways; apps define routes cleanly and securely.”
Given the Ingress-NGINX retirement timeline, migration should be part of your 2025–2026 cluster roadmap.
Do it once, do it cleanly — and you’ll gain a more scalable, future-proof networking layer.



