Ingress NGINX retirement: choose a replacement and test it
Existing routes keep answering once the community controller stops getting fixes. Compare replacements, audit annotations and test before changing DNS.
- Author
- The Log’in Line team
- Published
- Topics
- KubernetesGateway APIEnvoy

The Ingress NGINX retirement ended upstream maintenance in March 2026. Existing deployments keep routing traffic, but the community project no longer provides releases or security fixes. Replace the controller. You can keep the Ingress API with another implementation, or move to Gateway API. Choose by the behavior your applications need, especially authentication and NGINX annotations, rather than by how quickly a converter produces YAML.
For a small team, we recommend evaluating Gateway API first when planning a lasting replacement. Keeping Ingress with a maintained controller is reasonable when the migration must stay narrow. Neither path removes the need to test application traffic.
What stops in March 2026, and what keeps running
The retirement concerns the community kubernetes/ingress-nginx controller. It does not retire NGINX itself, the separate F5 NGINX Ingress Controller, or Kubernetes' Ingress API. The Kubernetes committee statement confirms the March 2026 retirement. An existing load balancer does not stop serving requests because that month has passed.
There is no upstream maintenance window left to plan around. A working deployment can still carry an unpatched component, and upgrading the Kubernetes control plane does not replace an ingress controller installed through your own Helm release.
Check provider-specific support separately. Microsoft states that critical security patches for the AKS application routing add-on's NGINX Ingress resources continue through November 2026. That exception does not cover an arbitrary ingress-nginx installation on AKS. As of November 2, 2026, it is the last stated month, not permission to postpone the project indefinitely. The documentation does not specify a day of the month.
Identify the controller before choosing its replacement
Inventory the controller image and its arguments, the IngressClass, and every application's annotations. A class called nginx is only a name: inspect its controller identifier and the running workload before concluding that it is the retired project.
These read-only examples use the networking.k8s.io/v1 API, kubectl 1.34.1 and jq 1.8.1. The JSON filters and conversion were tested offline on fixtures, not as an end-to-end cluster migration. Cluster queries require cluster-wide read permissions. Use a kubectl version compatible with your server.
kubectl get ingressclasses -o wide
kubectl get deployments,daemonsets -A -o json | jq -r '
.items[] | . as $w | .spec.template.spec.containers[] |
[$w.metadata.namespace, $w.metadata.name, .image,
((.args // []) | join(" "))] | @tsv'
kubectl get ingress -A -o json > ingress-inventory.json
jq -r '
.items[] |
[.metadata.namespace, .metadata.name,
(.spec.ingressClassName //
.metadata.annotations["kubernetes.io/ingress.class"] // "unset"),
((.metadata.annotations // {} | keys |
map(select(startswith("nginx.ingress.kubernetes.io/")))) |
join(","))] | @tsv' ingress-inventory.json
The workload listing deliberately includes all images: it avoids missing a controller mirrored into a private registry. Inspect arguments such as --controller-class and --ingress-class. Also check Helm values and global ConfigMaps for TCP/UDP mappings, proxy settings and snippets. The annotation report cannot see those dependencies.
For each hostname, record the owner, TLS secret, backend protocol and a request that must succeed. Add one request that must be rejected. An authentication migration passes only if an unauthenticated caller still cannot reach the protected backend.
The oversights that cause the most trouble in this kind of move rarely sit in the controller itself. A hand-written authentication annotation on a single Ingress, a TCP mapping left in a global ConfigMap, a rule that no longer matches because its class was not carried over: they tend to surface after cutover, rarely during testing.
Compare maintained Ingress and Gateway API options
Keeping Ingress changes fewer resource types; Gateway API separates infrastructure ownership from application routes. Both still need an installed controller. Gateway API CRDs alone do not create a functioning entry point.
| Candidate | When to evaluate it | Acceptance test before choosing |
|---|---|---|
| Maintained Ingress controller, such as Traefik or F5 NGINX Ingress Controller | You need to retain Ingress resources and limit the first migration | Check every annotation against that controller's documentation; sharing NGINX in a name proves no compatibility |
| Envoy Gateway | You want a Gateway API entry point built around Envoy | Validate required filters and the controller-specific policies for authentication or rate limiting |
| Kgateway | You are considering the implementation discussed in our existing Gateway API article | Test the exact version and extensions you intend to run; do not infer feature parity from its proxy |
| Traefik with Gateway API | Your team already operates Traefik and wants to reuse that knowledge | Check Gateway API support separately from its Ingress provider and middleware configuration |
| NGINX Gateway Fabric | You prefer a Gateway API implementation using an NGINX data plane | Verify the features of the selected edition and release, including any WAF requirement |
| Cloud-managed entry point | You prefer delegating controller lifecycle to the cloud provider | Confirm regional availability, supported routing features, charges and a way to leave the service |
This is a shortlist, not a performance ranking. Pin the controller chart, image and Gateway API CRD versions before testing; do not mix a latest documentation page with an older installed release. A conformance result describes a tested version and feature set. It does not certify support for your NGINX snippets.
For a new HTTP entry point, our proposed default is Gateway API, with Envoy Gateway and Kgateway in the evaluation. Our existing Kgateway article explains that implementation's design. Keep the decision narrower here: a team with simple Ingress rules and a tested maintained replacement may gain little from changing APIs during the same maintenance window.
Treat annotations as application behavior
An annotation has migrated only when the new entry point reproduces its intended behavior. A missing rewrite can return a plausible page from the wrong path; a missing authentication check can expose a private endpoint while every health probe stays green.
| Existing dependency | What to prove on the candidate |
|---|---|
rewrite-target and use-regex | Compare the backend path, capture groups and path precedence; regex rules are not automatically portable |
auth-url and auth-signin | Test denied requests, login redirects, callback URLs and identity headers |
proxy-body-size and timeout settings | Exercise an upload near the intended limit and a deliberately slow backend |
configuration-snippet and server-snippet | Explain each directive, then replace its behavior explicitly; arbitrary NGINX directives have no generic Gateway API equivalent |
| ModSecurity or another WAF dependency | Test the selected engine, rules and enforcement mode; successful routing says nothing about filtering |
Start with these dependencies before scoring products. If a required control has no supported replacement, either change the application design or select a different controller. Do not silently drop a security policy to finish the YAML conversion.
Convert offline, then validate a parallel entry point
ingress2gateway generates a starting point for review; it does not prove runtime equivalence. For ingress2gateway v1.2.0, the subcommand is print. The example below was tested offline against that release with an exported Ingress fixture. It does not install a controller or apply anything to the cluster.
ingress2gateway version
ingress2gateway print --providers=ingress-nginx \
--input-file=ingress-inventory.json \
--ingress-nginx-ingress-class=nginx \
--all-namespaces > gateway-candidate.yaml
Replace nginx with the source class from your inventory. Review the output's gatewayClassName against your installed target GatewayClass before applying anything. Save stderr diagnostics: our fixture produced YAML while warning that configuration-snippet was unsupported and proxy-body-size could not be applied by the standard emitter. Command success does not mean equivalent behavior. Review secrets, namespace boundaries and target-specific policies too.
Deploy the candidate with a separate address and explicit class selection. Check that neither controller claims the other's routes, and keep automated DNS tooling from publishing the candidate prematurely. Test its real TLS certificate and hostname before changing public DNS. With an IPv4 address, curl can direct a request to the candidate while preserving SNI and the HTTP host:
HOST=app.example.com
CANDIDATE_IP=203.0.113.20
curl --fail-with-body --resolve "${HOST}:443:${CANDIDATE_IP}" \
"https://${HOST}/health"
Those values are placeholders. Do not add -k: bypassing certificate validation would hide a failed TLS migration. A health endpoint checks only one path. Replay the protected requests, uploads, redirects and long-lived connections from your acceptance list. Inspect backend logs as well as HTTP status codes.

Move one hostname first. Define your own error-rate and latency thresholds from its baseline, then watch both entry points during DNS propagation. Keep the old configuration and certificates available for an agreed rollback period. Returning DNS to the previous address does not move cached clients or existing connections immediately. The retired controller is a temporary fallback, not a long-term recovery plan.
Budget the migration around exceptions, not route count
The work comes from distinct routing behaviors and their tests. Estimate inventory and annotation review, policy rewrites, parallel infrastructure, application testing, the cutover and the rollback rehearsal separately. There is no defensible universal number of days per cluster.
Record the duplicate load balancer and proxy capacity for the overlap period. Compute that cost from your provider's current hourly rates and the measured duration; add any edition or support subscription required for authentication or WAF features. Do not compare a free controller with a managed offer while excluding those dependencies.
Our recommendation: remove the unsupported controller first, with a named owner and a tested fallback. Gateway API is the preferred evaluation path for a lasting redesign; retained Ingress is a valid smaller change. If nobody owns the inventory or the cutover, our Kubernetes managed services can take over the operational work. Bring your controller image and annotation export to a free 30-minute diagnostic.
About the author
The Log’in Line team


