Ingress2Gateway 1.0: Migrating 50% of Clusters from ingress-nginx to Gateway API

Introduction
March 20, 2026 marks a turning point for Kubernetes networking. Ingress2Gateway 1.0 shipped as a stable migration assistant — just as the ingress-nginx controller enters its final maintenance window before retirement. If you're running ingress-nginx in production (industry surveys estimate ~50% of clusters), the question isn't whether to migrate, but how to do it without breaking traffic.
The retirement isn't hypothetical. After March 2026, ingress-nginx receives no further releases, bugfixes, or security patches. The repositories go read-only. Your existing deployments keep running, but you're on your own for vulnerabilities. SIG Network and the Security Response Committee were direct: this is unsustainable maintenance burden meeting insurmountable technical debt.
Ingress2Gateway 1.0 changes the migration equation. The tool jumped from supporting 3 ingress-nginx annotations to 30+ in this release — CORS, backend TLS, regex matching, path rewrite, timeouts. More importantly, it now backs each supported annotation with controller-level integration tests that verify behavioral equivalence, not just YAML structure. This is a migration assistant that tells you what it couldn't translate and why.
This article walks through the migration workflow: audit your cluster, convert with ingress2gateway, review the output, validate in a dev environment, run side-by-side with weighted traffic shifting, and cut over without downtime. You'll see which annotations translate directly, what requires manual intervention, and how to choose a Gateway API implementation that fits your team.
Why ingress-nginx is retiring
Two problems converged to make retirement inevitable.
The snippet security debt. Ingress NGINX became popular because you could inject raw NGINX configuration via annotations like nginx.ingress.kubernetes.io/configuration-snippet. Need a custom header? Add a snippet. Want rate limiting? Snippet. This flexibility powered billions of requests, but it's now viewed as a critical security liability — configuration injection attacks are trivial when users can inject arbitrary NGINX directives. Fixing this architectural "feature" would break backward compatibility for millions of deployments. The maintainers chose retirement over forced migration.
The maintainership gap. Despite its ubiquity, ingress-nginx was sustained by one or two maintainers working evenings and weekends. When security vulnerabilities move at internet speed, "best-effort maintenance" isn't defensible. SIG Network exhausted efforts to find additional maintainers. The retirement announcement itself failed to generate sustainable interest in taking over the project.
The timeline is clear: best-effort maintenance continues until March 2026. After that, the project joins the kubernetes-retired organization. Existing artifacts (Helm charts, container images) remain available, but no new CVEs will be patched.
warning: Check your cluster now:
kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx. If this returns pods, you have a migration to plan.
Ingress2Gateway 1.0 capabilities
Ingress2Gateway isn't a one-click replacement — it's a translation assistant with opinions. The tool reads your Ingress resources (and provider-specific CRDs), converts them to Gateway API resources, and emits warnings for unsupported features.
What changed in 1.0:
- 30+ annotations supported — up from 3. The big ones:
enable-cors,proxy-body-size,proxy-read-timeout,proxy-send-timeout,use-regex,ssl-redirect,force-ssl-redirect,configuration-snippet(warns, doesn't translate) - Integration testing — each supported annotation is backed by tests that spin up ingress-nginx and multiple Gateway API controllers, apply the same Ingress resource, translate with ingress2gateway, and verify runtime behavior matches. This catches edge cases like regex path matching differences and timeout semantics.
- Improved notifications — warnings are formatted clearly with source attribution and remediation suggestions. You'll see exactly which annotations didn't translate and why.
Supported providers: ingress-nginx (primary focus), Cilium, Kong, Apache APISIX, Istio, GCE, NGINX (F5), OpenAPI 3.0. If you're not on ingress-nginx, check the provider README for annotation coverage.
Emitters let you target implementation-specific extensions. The standard emitter outputs core Gateway API resources (Gateway, HTTPRoute). Add --emitter kgateway, --emitter envoy-gateway, or --emitter agentgateway to generate implementation-specific policies that capture otherwise-untranslatable annotations.
Here's a real example from the 1.0 release blog. An ingress-nginx manifest with 6 annotations:
# ingress-nginx.yaml (line 1-35)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
namespace: my-ns
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "1G"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-send-timeout: "1"
nginx.ingress.kubernetes.io/proxy-read-timeout: "1"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Request-Id: $req_id";
spec:
ingressClassName: nginx
rules:
- host: my-host.example.com
http:
paths:
- backend:
service:
name: website-service
port:
number: 80
path: /users/(\d+)
pathType: ImplementationSpecific
tls:
- hosts:
- my-host.example.com
secretName: my-secret
Running ingress2gateway print --input-file ingress-nginx.yaml --providers=ingress-nginx > gwapi.yaml produces Gateway and HTTPRoute resources. The CORS annotation translates directly to an HTTPRoute CORS filter. The regex path becomes type: RegularExpression with value (?i)/users/(\d+).* (ingress-nginx regex is case-insensitive prefix match — the tool adds the modifiers explicitly).
But you also get warnings:
┌─ WARN ────────────────────────────────────────
│ Unsupported annotation nginx.ingress.kubernetes.io/configuration-snippet
│ source: INGRESS-NGINX
│ object: Ingress: my-ns/my-ingress
└─
┌─ WARN ────────────────────────────────────────
│ ingress-nginx only supports TCP-level timeouts; i2gw has made a best-effort
│ translation to Gateway API timeouts.request. Please verify that this meets
│ your needs.
└─
┌─ WARN ────────────────────────────────────────
│ Failed to apply proxy-body-size: Most Gateway API implementations have
│ reasonable body size and buffering defaults
└─
The configuration-snippet annotation has no Gateway API equivalent — you'll need to check if your chosen implementation supports it via policies or custom resources. The timeout translation is best-effort (TCP-level to HTTP-level). The body size limit isn't translated because most Gateway implementations have reasonable defaults, but upload-heavy services should verify.
Migration workflow
The migration isn't a flip-the-switch operation. Plan for incremental validation with rollback at every step.
1. Audit
Identify the scope:
# Count Ingress resources
kubectl get ingress -A | wc -l
# List all ingress-nginx pods
kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx
# Export all Ingress manifests for review
kubectl get ingress -A -o yaml > all-ingresses.yaml
Document which namespaces, hosts, and annotations you're using. This becomes your migration checklist.
2. Convert
Run ingress2gateway against your manifests or live cluster:
# From files
ingress2gateway print --input-file all-ingresses.yaml --providers=ingress-nginx > gwapi.yaml
# From cluster (current namespace)
ingress2gateway print --providers=ingress-nginx > gwapi.yaml
# From cluster (all namespaces)
ingress2gateway print --providers=ingress-nginx --all-namespaces > gwapi.yaml
If you've chosen a Gateway API implementation, add the emitter flag: --emitter kgateway for kgateway-specific extensions, --emitter envoy-gateway for Envoy Gateway policies.
3. Review
This is the critical step. Read every warning. The tool tells you:
- Which annotations didn't translate (e.g.,
configuration-snippet) - Where it made best-effort translations (timeouts, body size)
- Behavioral differences you should verify (regex case sensitivity, URL normalization)
For each warning, decide: does the default behavior work, or do I need implementation-specific extensions? If you're using kgateway, the --emitter kgateway flag may capture some otherwise-untranslatable annotations via kgateway policies.
4. Validate in dev
Deploy the generated Gateway API resources to a development cluster running your chosen Gateway controller. Run integration tests against the new routes. Compare behavior to ingress-nginx:
- Does the CORS filter work as expected?
- Are regex paths matching correctly (case sensitivity)?
- Do timeouts trigger at the right point?
- Does URL normalization match your expectations?
Catch discrepancies here, not in production.
5. Parallel deployment
Run ingress-nginx and your Gateway API controller side-by-side. They can coexist — different ingress classes, different GatewayClasses. Use weighted DNS, cloud load balancer traffic splitting, or service mesh traffic management to gradually shift traffic from ingress-nginx to the Gateway API controller.
Start with 5% of traffic. Monitor error rates, latency, and application logs. Increase to 25%, then 50%, then 100% over days or weeks depending on your risk tolerance.

Figure 1: Traffic flows through a weighted DNS or load balancer that splits requests between ingress-nginx (old path, muted) and Gateway API controller (new path, highlighted). Both controllers route to the same backend services. Gradually shift the weights from 50/50 to 0/100 as you validate the new path.
6. Cutover
Once 100% of traffic flows through the Gateway API controller and you've validated for a suitable observation window (a week is common), delete the Ingress resources and uninstall ingress-nginx:
kubectl delete -f all-ingresses.yaml
helm uninstall ingress-nginx -n ingress-nginx
Keep the original Ingress manifests in version control — you'll want them as reference if you need to rollback (though with gradual traffic shifting, rollback is usually just a DNS weight change).
Annotation mapping — what translates and what doesn't
Understanding which annotations translate helps you anticipate manual work.
Direct mappings (Gateway API native):
| ingress-nginx annotation | Gateway API equivalent |
|---|---|
nginx.ingress.kubernetes.io/enable-cors |
HTTPRoute filters[].cors |
nginx.ingress.kubernetes.io/ssl-redirect |
HTTPRoute filters[].requestRedirect with scheme: https |
nginx.ingress.kubernetes.io/force-ssl-redirect |
HTTPRoute filters[].requestRedirect |
nginx.ingress.kubernetes.io/use-regex |
HTTPRoute matches[].path.type: RegularExpression |
Best-effort translations:
| Annotation | Translation | Caveat |
|---|---|---|
nginx.ingress.kubernetes.io/proxy-read-timeout |
HTTPRoute timeouts.request |
ingress-nginx uses TCP-level timeouts; Gateway API uses HTTP-level request timeout. Translation combines read+send into single request timeout. |
nginx.ingress.kubernetes.io/proxy-send-timeout |
HTTPRoute timeouts.request |
Same as above. |
nginx.ingress.kubernetes.io/proxy-body-size |
Not translated | Most Gateway implementations have reasonable defaults. Upload-heavy services should verify implementation-specific policies. |
Unsupported (require manual intervention):
| Annotation | Why unsupported | Workaround |
|---|---|---|
nginx.ingress.kubernetes.io/configuration-snippet |
Arbitrary NGINX config injection — no Gateway API equivalent | Check implementation docs. kgateway supports some via policies. May require custom Envoy filters. |
nginx.ingress.kubernetes.io/custom-header (via snippet) |
Header manipulation is supported, but snippet-based injection isn't | Use HTTPRoute filters[].requestHeaderModifier or responseHeaderModifier |
| URL normalization configuration | Gateway API doesn't standardize this | Test your implementation (Agentgateway, Envoy Gateway, kgateway, Istio all differ) |
The 1.0 release supports over 30 annotations, but the ones above are the most common in production deployments. Check the ingress2gateway documentation for the complete list.
Choosing a Gateway API implementation
Gateway API is a specification — you need an implementation to run it. The four major options:
kgateway (formerly Gloo Gateway) — Envoy-based, donated to CNCF in early 2025 after 7+ years production use. Best for enterprise scale and AI workloads. The control plane uses the krt framework (from Istio) for incremental config updates — handles 10,000+ routes without snapshot bottlenecks. Includes Agentgateway component for LLM traffic with Model Context Protocol support and token-based rate limiting. Native Istio integration for mTLS.
Envoy Gateway — Community-driven, strict conformance to Gateway API spec. Appeals to teams wanting standards without vendor lock-in. Also Envoy-based. The companion Envoy AI Gateway project adds LLM routing capabilities. Good choice for teams valuing community governance and multi-vendor environments.
NGINX Gateway Fabric — F5's Gateway API implementation using NGINX as the data plane. Best for teams with deep NGINX expertise or existing F5 investments. High throughput, integrates with F5 enterprise security tools. The learning curve is gentle for NGINX teams.
Traefik — Single binary, no external dependencies. Focuses on simplicity and developer experience. Declarative configuration fits GitOps workflows well. Gentle learning curve, large community. Best for teams prioritizing operational simplicity over enterprise features.
Decision framework:
- Enterprise scale + AI workloads → kgateway
- Standards-focused + multi-vendor → Envoy Gateway
- NGINX expertise + F5 ecosystem → NGINX Gateway Fabric
- Simplicity + rapid iteration → Traefik
All four are production-ready. The choice depends on your team's background and future roadmap (especially AI/LLM support).
Gotchas
These are the issues that catch teams mid-migration:
Regex paths are case-insensitive prefix matches in ingress-nginx. The tool translates /users/(\d+) to (?i)/users/(\d+).* — adding case-insensitive modifier and trailing wildcard. Most organizations want exact, case-sensitive matches in Gateway API. Change the value to /users/(\d+) and type to RegularExpression after generation.
Timeout semantics differ. ingress-nginx supports separate read and send timeouts at the TCP level. Gateway API has timeouts.request at the HTTP level. The translation is best-effort. If your service needs sub-second timeouts, verify the translated value matches your SLOs.
Body size limits aren't standardized. Most Gateway implementations default to 1MB or similar. If you're accepting large uploads (the example showed 1G), verify your implementation's default and configure via implementation-specific policy if needed.
URL normalization varies. Gateway API implementations handle URL normalization differently (RFC 3986, Section 6). Agentgateway, Envoy Gateway, kgateway, and Istio all have different behaviors. Test with your actual URL patterns before committing.
Cross-namespace routing requires ReferenceGrant. Gateway API enforces explicit permission for an HTTPRoute in one namespace to reference a Service in another. If your ingress-nginx deployment routes across namespaces, you'll need to create ReferenceGrant resources. This is a security feature, not a bug — but it's a new config item to manage.
Listeners are explicit. Ingress has implicit HTTP and HTTPS entry points. Gateway API requires you to define a Gateway resource with explicit listeners[] blocks. More control, more configuration. You specify ports, protocols, hostnames, and TLS termination explicitly.
Wrap-up
The ingress-nginx retirement is a forcing function — migrate to Gateway API or accept unmaintained infrastructure. Ingress2Gateway 1.0 makes the technical migration doable: 30+ annotations supported, integration-tested translation, clear warnings about what needs manual intervention.
Start your audit this week. Run ingress2gateway against your manifests. Review the output, validate in dev, plan parallel deployment. The March 2026 deadline gives you runway, but the best migrations are incremental and deliberate.
Gateway API isn't just a replacement — it's a more expressive API designed for how teams actually operate Kubernetes. Role-oriented resources (GatewayClass, Gateway, HTTPRoute), native support for advanced routing, implementation-specific extensions when needed. The migration is work. But Gateway API gives you structured routing, role-oriented resources, and a controller ecosystem that won't retire on you.
Mastering the Kubernetes ecosystem — depth-first, no hype.
Subscribe to KubeDojo
Get the latest articles delivered to your inbox.
Related Articles

llm-d Joins CNCF Sandbox: Kubernetes-Native Distributed LLM Inference
llm-d was accepted as a CNCF Sandbox project, providing Kubernetes-native distributed inference with KV-cache-aware routing, prefill/decode disaggregation, and accelerator-agnostic serving.

AI Gateway Working Group and Gateway API Inference Extension GA
Gateway API Inference Extension GA standardizes model-aware routing for self-hosted LLMs with KV-cache-aware scheduling and LoRA adapter affinity.