Ingress-NGINX reached retirement on 24 March 2026. The Kubernetes Steering and Security Response Committees say there will be no further releases, bug fixes, or security patches for the project. That does not mean every cluster must replace its ingress controller during an incident, but it does mean that keeping it as an Internet-facing dependency needs an explicit migration plan.
This guide converts a small, ordinary Ingress-NGINX route set into Gateway API resources without deleting the existing ingress. The finished result is a reviewed Gateway and HTTPRoute manifest, attached to a controller-supported GatewayClass, with a test request that proves the hostname, path, backend, and TLS behavior before traffic is moved. The commands are documentation-verified examples, not output from a particular cluster. Use a maintenance window and adapt the controller installation, load-balancer addresses, namespace names, and DNS records to the platform you operate.
Treat the migration as a routing change
Ingress is a stable Kubernetes API, but the Ingress resource intentionally leaves much controller behavior to implementation-specific annotations. Gateway API separates the infrastructure-facing listener from the application-facing route. A GatewayClass represents a controller implementation, a Gateway declares listeners, and an HTTPRoute supplies host, path, filter, and backend rules.
That separation is useful during a migration because the controller owner can approve the public listener while an application namespace owns only the route it attaches. It also means a converted manifest is not automatically a production-ready replacement. Redirect annotations, authentication integrations, snippets, rate limits, WAF policy, rewrite behavior, and load-balancer settings may be controller-specific. Inventory them before changing DNS or retiring an existing controller.
Do not use the conversion tool as a reason to remove Ingress-NGINX first. Keep the current route serving traffic until the Gateway path has been tested and an owner has approved the cutover and rollback plan.
Establish a safe inventory
Start by listing the Ingress objects and the controller class they select. The first command does not change cluster state. The second captures the objects in a local file so conversion and review can happen without repeatedly reading the production API.
kubectl get ingress -A
kubectl get ingress -A -o yaml > ingress-export.yaml
Treat ingress-export.yaml as operational data. Ingress annotations can reveal hostnames, internal paths, identity-provider locations, and other deployment details. Store it in the same protected location as the rest of the change record, and do not commit it to a public repository.
Review every spec.ingressClassName, legacy kubernetes.io/ingress.class annotation, host, pathType, backend service, TLS secret, and controller annotation. In particular, make a separate list of annotations beginning with nginx.ingress.kubernetes.io/. The core Ingress fields are good candidates for translation; an annotation needs an equivalent supported policy or filter in the chosen Gateway implementation. A controller-specific annotation copied into a Gateway manifest is not a migration.
Also record the externally visible behavior before changing it. For each hostname and important path, capture the expected status code, redirect target, response headers, and backend health endpoint. A simple request from an approved test network can provide a baseline:
curl -i --resolve app.example.net:443:<current-lb-ip> \
https://app.example.net/healthz
Replace the hostname and address with values owned by your organization. Save only the facts needed to compare the new path; redact cookies, authorization headers, and response bodies containing customer data.
Choose and check the Gateway implementation
Gateway API defines the portable resource model, but it does not provide a data plane by itself. Install and operate a Gateway API implementation approved for the cluster before applying application routes. The implementation documentation determines its GatewayClass name, how it provisions an address, whether it supports the filters you need, and how it exposes status and metrics.
Confirm that the Gateway API resources and at least one controller-provided class exist:
kubectl api-resources | grep gateway.networking.k8s.io
kubectl get gatewayclass
The first command should show the Gateway API kinds, including gatewayclasses, gateways, and httproutes. The second should return a class that the controller reports as accepted. If either condition is missing, stop here and have the platform owner install or repair the Gateway implementation. Applying routes before a controller is ready only creates objects with no functioning listener.
For this example, assume the supported class is named <gateway-class> and the application is in the web namespace. Create a listener first. allowedRoutes limits attachment to routes in the same namespace, which is a conservative default for a first migration. Cross-namespace routes require an explicit attachment policy and should be reviewed separately.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: web-gateway
namespace: web
spec:
gatewayClassName: <gateway-class>
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: app.example.net
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: app-example-net-tls
allowedRoutes:
namespaces:
from: Same
The TLS secret must exist in the Gateway namespace and use the form required by the implementation. Do not create a placeholder secret or move a production certificate casually. Confirm the certificate-management owner agrees that this listener is allowed to reference the named secret.
Generate a candidate route, then review it
Ingress2Gateway is maintained by the Gateway API SIG-Network project. The project reads Ingress resources or a manifest file and emits Gateway API YAML, JSON, or KYAML. Its release 1.2.0 was published on 7 July 2026. The converter is useful because it makes field mapping visible, not because it guarantees equivalence.
Run it against the exported file rather than a live cluster for the first pass. Use the current release binary or an approved package method, then produce a candidate YAML file:
ingress2gateway print \
--input-file ingress-export.yaml \
--providers=ingress-nginx \
--output yaml > gateway-candidate.yaml
The tool warns about untranslated fields and unsupported features. Treat each warning as a migration blocker until it has a documented replacement, an accepted removal, or a controller-specific design approved by the service owner. The project explicitly does not copy Ingress annotations into Gateway API resources.
Inspect the candidate before applying it. For an ordinary host-and-path rule, verify these translations: an Ingress host becomes an HTTPRoute.spec.hostnames entry; Exact becomes an exact path match; Prefix becomes PathPrefix; and a service backend becomes a backendRefs entry. Check service names and ports against the live Service rather than assuming the generated reference is correct.
A minimal reviewed route can look like this:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
namespace: web
spec:
parentRefs:
- name: web-gateway
sectionName: https
hostnames:
- app.example.net
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 8080
parentRefs attaches the route to the named listener, sectionName avoids an accidental attachment to a future listener, and backendRefs identifies the Service port exposed to the Gateway. This example intentionally contains no rewrite, redirect, authentication, or timeout filter. Add such behavior only after confirming the selected controller supports the relevant Gateway API feature and after testing its effect.
Apply without cutting over
Apply the reviewed Gateway and route into a non-production namespace or a production namespace that uses a separate test hostname. Validate client-side syntax first, then apply the files in dependency order:
kubectl apply --dry-run=client -f gateway.yaml
kubectl apply --dry-run=client -f route.yaml
kubectl apply -f gateway.yaml
kubectl apply -f route.yaml
kubectl get gateway,httproute -n web
A successful API response does not prove that the data plane has accepted the configuration. Inspect status conditions on both objects:
kubectl describe gateway web-gateway -n web
kubectl describe httproute web-route -n web
Look for an accepted and programmed listener on the Gateway, and an accepted route with a resolved backend reference. The exact condition messages are controller-specific. A missing address, an unaccepted parent reference, or an unresolved backend is a reason to stop and diagnose the configuration, not to change public DNS.
Once the Gateway reports an address, test it while preserving the production hostname in TLS and HTTP routing. For a controller that supplies an IP address, use --resolve with the new address:
curl -i --resolve app.example.net:443:<gateway-lb-ip> \
https://app.example.net/healthz
Compare the status, certificate subject and issuer, redirect behavior, critical headers, and application response with the baseline. Exercise each important prefix, exact route, and error path. For an authenticated application, use a dedicated test account and avoid storing session material in the change record.
Cut over and retain a rollback path
Only after the parallel path is accepted should the DNS or load-balancer change be scheduled. Lower DNS TTLs ahead of the window if that fits local policy, publish the new target, and monitor both controller metrics and application errors during the transition. Do not delete the working Ingress resource as part of the first cutover.
The rollback condition should be concrete: a failed health check, incorrect TLS identity, unexpected 4xx or 5xx rate, missing route, or a regression in a required security control. If it occurs, restore the previous DNS or load-balancer target using the recorded change procedure. Leave the Gateway resources in place for diagnosis unless the incident process requires otherwise.
Ingress-NGINX retirement is a reason to replace an unmaintained edge component, not a reason to make routing changes without evidence. Inventory controller-specific behavior, translate only the portable rules, validate Gateway and route status, and test the new listener before moving traffic. That produces a migration record that can be expanded service by service instead of a controller replacement performed in one risky change.
Sources
- Ingress NGINX: Statement from the Kubernetes Steering and Security Response Committees, Kubernetes, 29 January 2026.
- Announcing Ingress2Gateway 1.0: Your Path to Gateway API, Kubernetes, 20 March 2026.
- Ingress2Gateway README and supported providers, SIG-Network project documentation, accessed 29 July 2026.
- Gateway API, Kubernetes documentation, accessed 29 July 2026.