Ingress-NGINX Is Being Retired: How to Migrate to Gateway API Before It Breaks
Ingress-NGINX is officially being retired. Your ingress rules will stop working. Here's the step-by-step migration plan to Kubernetes Gateway API before it's too late.
The announcement is official: Ingress-NGINX is being retired. If you're running it in production — and there's a good chance you are, since it powers over 40% of all Kubernetes ingress traffic — you need a migration plan now.
This isn't a "we'll deprecate it eventually" situation. The maintainers have clearly stated that Ingress-NGINX will receive only critical security patches going forward. No new features. No bug fixes. The future is Gateway API.
Let me show you exactly how to migrate without breaking your traffic.
Why Ingress-NGINX Is Being Retired
The Kubernetes Ingress resource was designed in 2015. It was simple — map a hostname and path to a backend service. But modern traffic routing needs have far outgrown what the Ingress spec can express:
- No traffic splitting — canary deployments require annotations hacks
- No header-based routing — need custom NGINX snippets
- No TCP/UDP support — only HTTP/HTTPS
- No cross-namespace routing — everything must be in the same namespace
- Annotation sprawl — every controller invented its own annotation language
Gateway API solves all of these with a proper, extensible specification.
Step 1 — Audit Your Current Ingress Rules
First, find every Ingress resource in your cluster:
kubectl get ingress -A -o wideExport them for reference:
kubectl get ingress -A -o yaml > ingress-backup.yamlCategorize your ingresses:
# Count by namespace
kubectl get ingress -A --no-headers | awk '{print $1}' | sort | uniq -c | sort -rn
# Check which ones use TLS
kubectl get ingress -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.spec.tls[*].hosts[*]}{"\n"}{end}'Document any custom annotations — these are the tricky part of migration:
kubectl get ingress -A -o json | jq -r '.items[].metadata.annotations // {} | keys[]' | sort -uStep 2 — Install Gateway API CRDs
Gateway API is not installed by default. Install the standard channel CRDs:
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yamlVerify installation:
kubectl get crd | grep gatewayYou should see:
gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.io
Step 3 — Choose Your Gateway Controller
You need a controller that implements Gateway API. Popular options:
| Controller | Best For | Gateway API Support |
|---|---|---|
| Envoy Gateway | Cloud-native, full-featured | Full v1.2 |
| Cilium | eBPF performance, no sidecar | Full v1.2 |
| NGINX Gateway Fabric | NGINX familiarity | Full v1.2 |
| Traefik | Existing Traefik users | Full v1.2 |
| HAProxy Kubernetes Ingress | High-throughput TCP | Partial |
Install Envoy Gateway (recommended for new setups):
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.2.0 \
-n envoy-gateway-system \
--create-namespaceStep 4 — Create the Gateway Resource
The Gateway replaces your old ingress controller's LoadBalancer service:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: default
spec:
gatewayClassName: eg # matches your controller
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls
kind: Secret
allowedRoutes:
namespaces:
from: AllStep 5 — Convert Ingress Rules to HTTPRoutes
Here's the translation pattern:
Old Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080New HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
spec:
parentRefs:
- name: main-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-service
port: 8080Step 6 — Handle Common Annotation Migrations
Rate Limiting
# Old: nginx.ingress.kubernetes.io/limit-rps: "10"
# New: BackendTrafficPolicy (Envoy Gateway)
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: rate-limit
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: my-app
rateLimit:
type: Local
local:
rules:
- limit:
requests: 10
unit: SecondCORS Headers
# Old: nginx.ingress.kubernetes.io/enable-cors: "true"
# New: HTTPRoute filter
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: Access-Control-Allow-Origin
value: "*"SSL Redirect
# Old: nginx.ingress.kubernetes.io/ssl-redirect: "true"
# New: HTTPRoute redirect filter
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301Step 7 — Run Both in Parallel
Don't do a big-bang migration. Run both controllers simultaneously:
- Deploy Gateway controller alongside Ingress-NGINX
- Create HTTPRoutes that mirror your Ingress rules
- Update DNS to point to the new Gateway's LoadBalancer IP
- Monitor traffic on both controllers
- Once Gateway handles all traffic, remove Ingress resources
- Uninstall Ingress-NGINX
# Check Gateway's external IP
kubectl get gateway main-gateway -o jsonpath='{.status.addresses[0].value}'
# Verify HTTPRoute is attached
kubectl get httproute my-app -o jsonpath='{.status.parents[0].conditions[*].type}'Migration Checklist
| Task | Status |
|---|---|
| Export all current Ingress resources | ☐ |
| Document custom annotations used | ☐ |
| Install Gateway API CRDs | ☐ |
| Install Gateway controller | ☐ |
| Create Gateway resource with TLS | ☐ |
| Convert Ingress rules to HTTPRoutes | ☐ |
| Migrate annotation logic to policies | ☐ |
| Test with curl/browser against new Gateway | ☐ |
| Switch DNS to Gateway LoadBalancer | ☐ |
| Monitor for 48 hours | ☐ |
| Remove old Ingress resources | ☐ |
| Uninstall Ingress-NGINX | ☐ |
Wrapping Up
Ingress-NGINX served us well for years, but its time is up. Gateway API is the official future of Kubernetes traffic routing — it's more powerful, more standardized, and actively maintained.
Don't wait until Ingress-NGINX stops getting security patches. Start your migration now while you can run both in parallel.
Want to master Kubernetes networking, Gateway API, and production traffic management with hands-on labs? The KodeKloud Kubernetes course covers all of this with real cluster environments. If you need a managed Kubernetes cluster to test your migration, DigitalOcean's managed Kubernetes gives you a production-ready cluster in minutes.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
Kubernetes DNS Not Working: How to Fix CoreDNS Failures in Production
Pods can't resolve hostnames? Getting NXDOMAIN or 'no such host' errors? Here's how to diagnose and fix CoreDNS issues in Kubernetes step by step.
Kubernetes DNS Resolution Failures — How to Fix CoreDNS Issues
Fix Kubernetes DNS resolution failures caused by CoreDNS misconfigurations, ndots issues, and pod DNS policies. Real troubleshooting scenarios with step-by-step solutions.
Nginx Ingress 502 Bad Gateway — How to Fix It (2026)
Getting 502 Bad Gateway from your Nginx Ingress Controller? Here's every cause and the exact fix for each one.