TL;DR: If you run ingress-nginx in production, treat March 2026 as a hard deadline. After retirement, you should not expect new releases or security fixes. Start migrating to Kubernetes Gateway API (or another supported ingress/gateway implementation) now.
Quick checklist
- List every Ingress + annotations
- Identify snippet usage
- Install Gateway API CRDs
- Choose a Gateway API implementation
- Convert 1 low-risk service first
- Run parallel + test
- Cut over with rollback ready
- Repeat service-by-service
Why you should care?
If your cluster’s north–south traffic depends on Ingress-NGINX, you’re likely relying on:
- TLS termination
- path/host routing
- HTTP header rewrites
- rate limits / auth integrations
- “magic” annotation behaviors
Retirement means: your current setup may keep running… until a CVE, a Kubernetes upgrade, or a cloud load balancer change turns it into a pager storm.
Ingress vs Gateway API
Ingress was a simple contract:
- one resource (
Ingress) - annotations for “advanced” behaviors
- controller-specific knobs
Gateway API splits responsibilities and makes traffic management portable across implementations:
-
GatewayClass— which controller provides gateway capability -
Gateway— the actual data-plane entry point (listeners, TLS, addresses) -
HTTPRoute/TCPRoute/GRPCRoute— routing rules - policies (e.g., TLS policies) — portable behaviors that don’t require annotations
How Resources are connected?
Migration plan
Step 0 — Inventory what you’re actually using
Most Ingress-NGINX clusters are annotation-heavy.
Run these to find your “hidden complexity”:
# List all ingresses and controllers
kubectl get ingress -A
# Show every annotation in ingresses (quick scan)
kubectl get ingress -A -o json | jq -r '
.items[] |
"\(.metadata.namespace)/\(.metadata.name)\t" +
((.metadata.annotations // {}) | to_entries | map("\(.key)=\(.value)") | join("; "))
'
# Find the scary ones (snippets, custom templates)
kubectl get ingress -A -o json | jq -r '
.items[] |
select((.metadata.annotations // {}) | has("nginx.ingress.kubernetes.io/server-snippet")
or has("nginx.ingress.kubernetes.io/configuration-snippet")) |
"\(.metadata.namespace)/\(.metadata.name)"
'
Make a list of:
- TLS behavior (single cert? wildcard? per-host?)
- rewrites and redirects
- auth (OIDC, basic auth, external auth)
- rate-limits
- custom NGINX snippets
Rule: the more snippets you have, the more you must treat this as an application migration—not just YAML changes.
Step 1 — Install Gateway API CRDs
You can install the Gateway API CRDs via your preferred method (Helm / kustomize / manifests), but don’t attach production traffic yet.
Verify CRDs exist:
kubectl get crd | grep gateway
kubectl api-resources | grep -E 'gateway|httproute|tcproute|grpcroute'
Step 2 — Pick an implementation
Gateway API is a spec. You still need an implementation.
Common choices:
- Managed gateways (cloud-provider)
- Service mesh gateways
- Dedicated gateway controllers
Pick based on:
- features you need (TCP/UDP? WAF? mTLS?)
- operational complexity
- observability and rate-limiting
- support and upgrade story
Step 3 — Convert one Ingress
Let’s migrate a typical Ingress:
Before: Ingress-NGINX
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: prod
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: app-svc
port:
number: 80
After: Gateway + HTTPRoute
Note: the exact fields can vary slightly by implementation.
Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: prod-gateway
namespace: prod
spec:
gatewayClassName: example-gatewayclass
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: app.example.com
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: app-tls
HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: prod
spec:
parentRefs:
- name: prod-gateway
hostnames:
- app.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: app-svc
port: 80
Rewrite behavior: what to do instead of rewrite-target
Ingress-NGINX had lots of rewrite tricks.
In Gateway API, you’ll typically use filters (implementation support varies):
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: prod
spec:
parentRefs:
- name: prod-gateway
hostnames:
- app.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: app-svc
port: 80
If your rewrite logic depends on regex capture groups (/$1), you may need to:
- refactor routes
- move logic into the app
- or pick an implementation that supports your needs
Step 4 — Run both in parallel
Option A: Separate DNS names (cleanest)
- Keep
app.example.comon Ingress-NGINX - Create
app-gw.example.comon Gateway - Test, load-test, verify metrics
- Switch DNS once stable
Option B: Weighted traffic (advanced)
Some gateways support traffic splitting between backends or routes.
If supported, roll out progressively:
- 1% → 10% → 50% → 100%
Step 5 — Validate
Functional checks
# TLS and SNI
curl -vk https://app.example.com/
# Host routing
curl -H 'Host: app.example.com' https://<gateway-lb-ip>/
# Path behavior
curl -I https://app.example.com/api/health
Load and latency checks
- p95/p99 latency
- error rate (4xx/5xx)
- upstream timeouts
- connection reuse
Common pitfalls
- Annotation-only features: Snippets and custom NGINX directives rarely translate 1:1.
- Auth flows: External auth in NGINX may need a dedicated auth service or gateway plugin.
- TLS edge cases: Wildcard certs + SNI + multiple hosts = test carefully.
-
Path matching differences:
Prefixvs regex behaviors differ. - Observability: Make sure you have equivalent access logs, metrics, tracing.
Rollback plan
- Keep Ingress-NGINX resources intact during migration.
- Make DNS changes reversible.
- Use low TTLs during cutover.
- Maintain a “back to old gateway” runbook.
Rollback should be one command or one DNS change, not a 2-hour YAML surgery.

Top comments (0)