Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This article describes how to migrate workloads from the application routing add-on with ingress-nginx to the application routing Gateway API implementation, which serves traffic through the Kubernetes Gateway API. The two data planes can run side-by-side on the same AKS cluster, which lets you perform a zero-downtime cutover by shifting client traffic with DNS instead of an in-place swap.
Why migrate
Caution
The Kubernetes SIG Network and the Security Response Committee announced the upcoming retirement of the Ingress NGINX project, with maintenance ending in March 2026. There's no immediate action required today for AKS clusters using the application routing add-on with NGINX. Microsoft will provide official support for critical security patches for Application Routing add-on NGINX Ingress resources through November 2026.
AKS is aligning with upstream Kubernetes by moving to Gateway API as the long-term standard for ingress and L7 traffic management. We recommend you start planning your migration path based on your current setup:
- Application routing add-on users: Production workloads remain fully supported through November 2026. Migrate to the application routing Gateway API implementation for a Gateway API-based ingress traffic management experience.
- OSS NGINX users have several options:
- Migrate to the application routing add-on with NGINX to benefit from official support through November 2026 while planning your long-term Gateway API migration.
- Migrate to the application routing Gateway API implementation for a Gateway API-based ingress traffic management experience.
- Service mesh users: If you plan to adopt a service mesh, consider the Istio-based service mesh add-on. Use Istio Ingress today, and plan to migrate to Istio Gateway API support when it becomes GA.
Microsoft-supported security patches for the ingress-nginx-based application routing add-on end in November 2026. The supported successor on AKS is the application routing Gateway API implementation, which deploys a lightweight, Istio control plane that only reconciles Gateway API resources belonging to the approuting-istio GatewayClass, with no support for Istio CRDs or data-plane sidecar injection.
How the migration works
The ingress-nginx add-on and the Gateway API implementation deployments/services each get their own Azure Load Balancer frontend IP. Clients keep hitting the ingress-nginx IP until the hostname they resolve points to the Gateway API proxy.
There's no in-place flag that swaps the ingress-nginx load balancer IP for the Gateway API one. The strategy is to run both data planes in parallel, validate the new path, then move clients to the new IP. If an upstream system (Azure Front Door origin, Traffic Manager endpoint, firewall allow list) pins the existing ingress-nginx IP, update that upstream configuration to the new Gateway IP rather than trying to reassign the existing IP—AKS deletes its managed public IPs when their owning Service is removed even if you mark them Static, so you can't perform an in-place IP swap safely.
Key differences
| Aspect | ingress-nginx add-on | Gateway API (Istio) implementation |
|---|---|---|
| API | networking.k8s.io/v1 Ingress |
gateway.networking.k8s.io/v1 Gateway and HTTPRoute |
| Az CLI Enable flag | --enable-app-routing |
--enable-app-routing-istio (use with --enable-gateway-api; the Managed Gateway API add-on is required for support) |
| Class name | webapprouting.kubernetes.azure.com |
approuting-istio |
| Data plane Service | nginx in app-routing-system |
<gateway-name>-approuting-istio in the Gateway's namespace |
| Azure DNS / Key Vault TLS wiring | Built in | Not yet supported. See Secure application routing Gateway API ingress traffic |
Prerequisites
Azure CLI 2.86.0 or later.
If the cluster has the Istio service mesh add-on enabled, disable it and remove the leftover
networking.istio.ioCRDs and theistioGatewayClassbefore you start. The application routing Gateway API control plane fails to start while those CRDs are present. See Limitations for the cleanup command.
Set environment variables that the rest of this article uses:
export RG=<resource-group>
export CLUSTER=<cluster-name>
Step 1: Inventory current ingress
List every Ingress that uses the ingress-nginx class and the hostnames pointing at the ingress-nginx load balancer IP. Include any upstream references such as Azure Front Door origins, Traffic Manager endpoints, and firewall allow lists.
kubectl get ingress -A -o jsonpath='{range .items[?(@.spec.ingressClassName=="webapprouting.kubernetes.azure.com")]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'
Capture the current ingress-nginx IP for later validation:
INGRESS_NGINX_IP=$(kubectl get svc -n app-routing-system nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
Step 2: Lower the DNS TTL
Note
Skip this step if your clients reach the cluster by IP rather than DNS—the cutover in step 7 is a DNS change, so a low TTL is only relevant when DNS is in the path.
For each hostname that resolves to INGRESS_NGINX_IP, lower the record TTL to 60 seconds (or the smallest value your provider allows). Wait for the previous TTL window to fully expire before you continue. If the previous TTL was one hour, wait at least one hour. Skipping this step means cached resolvers continue sending clients to ingress-nginx after you delete the Ingress.
Step 3: Enable the Gateway API implementation
Enable the Managed Gateway API CRDs and the application routing Gateway API implementation. Ingress-nginx continues serving traffic.
az aks update --resource-group $RG --name $CLUSTER --enable-gateway-api
az aks update --resource-group $RG --name $CLUSTER --enable-app-routing-istio
Verify the control plane and GatewayClass are ready:
kubectl -n aks-istio-system rollout status deploy/istiod --timeout=5m
kubectl get gatewayclass approuting-istio
Step 4: Translate each Ingress to Gateway and HTTPRoute
For each Ingress, create a matching Gateway and HTTPRoute. For per-field translation guidance—including how to convert path matches, redirects, rewrites, and header manipulation—follow the Kubernetes Gateway API migration guide. To bulk-translate existing manifests instead of writing them by hand, consider ingress2gateway, a Kubernetes SIG Network tool that reads Ingress resources (including ingress-nginx annotations) and emits equivalent Gateway API resources—treat its output as a starting point and review before applying.
The following example converts an ingress-nginx Ingress for host httpbin.contoso.com with path prefix /get and backend httpbin:8000. For reference, the source Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin
namespace: demo
spec:
ingressClassName: webapprouting.kubernetes.azure.com
rules:
- host: httpbin.contoso.com
http:
paths:
- path: /get
pathType: Prefix
backend:
service:
name: httpbin
port:
number: 8000
The equivalent Gateway and HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: httpbin-gateway
namespace: demo
spec:
gatewayClassName: approuting-istio
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
namespace: demo
spec:
parentRefs:
- name: httpbin-gateway
hostnames: ["httpbin.contoso.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /get
backendRefs:
- name: httpbin
port: 8000
Wait for the Gateway to program and capture its public IP:
kubectl wait --for=condition=Programmed -n demo \
gateways.gateway.networking.k8s.io/httpbin-gateway --timeout=5m
GW_IP=$(kubectl get gateway -n demo httpbin-gateway -o jsonpath='{.status.addresses[0].value}')
Step 5: Provision TLS on the Gateway
If the original Ingress terminated HTTPS, configure TLS on the new Gateway before you change DNS. Follow Secure application routing Gateway API ingress traffic to sync the certificate from Azure Key Vault by using the Secrets Store CSI driver, then add a TLS listener that references the resulting Kubernetes Secret through certificateRefs. A native Azure Key Vault integration for the application routing Gateway API implementation is in development; until it ships, the preceding Secrets Store CSI driver workflow is the supported path.
Step 6: Validate the Gateway before flipping client traffic
Probe both data planes by pinning DNS resolution with curl --resolve. Both should return 200 continuously while you run the probe in a loop for several minutes.
Important
Don't cut over until you fully exercise the Gateway IP. The DNS flip in step 7 is the point at which real client traffic lands on the new data plane—anything broken on the Gateway side becomes a customer-visible outage at that moment. Defects that are easy to find by hitting GW_IP directly are much harder to triage once production traffic is mixed in.
# New Gateway path
curl -sS -o /dev/null -w '%{http_code}\n' \
--resolve "httpbin.contoso.com:80:${GW_IP}" \
http://httpbin.contoso.com/get
# Old ingress-nginx path (should still work)
curl -sS -o /dev/null -w '%{http_code}\n' \
--resolve "httpbin.contoso.com:80:${INGRESS_NGINX_IP}" \
http://httpbin.contoso.com/get
If you added a TLS listener in step 5, also probe HTTPS by switching the port to 443 and the scheme to https://.
At minimum, verify the following items against GW_IP (using --resolve or a Host header) before you flip DNS:
- Every hostname and route that the original
Ingressserved, not just one happy-path URL. Routes that share a host but differ in path prefix, header match, or method must each be hit. - HTTPS end-to-end with the production certificate served by the
Gateway. Verify the certificate chain and SAN list match what clients expect; don't rely on--insecure. - Application behavior, not just HTTP status codes. Submit POST bodies, exercise authentication, and confirm long-running responses (streaming, WebSockets, gRPC) work end-to-end if your workload uses them.
- Sustained load for at least several minutes from a probe loop, watching for intermittent 5xx responses, latency regressions, or
Gateway/HTTPRoutestatus conditions flapping. - Upstream integrations in a nonproduction configuration where possible—for example, point a staging Front Door origin or Traffic Manager endpoint at
GW_IPand verify health probes pass.
Only proceed to step 7 once every preceding check passes consistently. If anything fails, fix it on the Gateway side while ingress-nginx is still serving real clients—you have unlimited rollback room until DNS changes.
Step 7: Cut over
- Update the DNS A record for the hostname to point to
GW_IP. Because you already lowered the TTL in step 2, drain happens within minutes. - If any upstream system pins the ingress-nginx IP—for example, an Azure Front Door origin, a Traffic Manager endpoint, or a customer firewall allow list—update it to
GW_IPat the same time. Don't try to reassign the existing IP to the Gateway: the cluster's cloud provider creates and manages the ingress-nginx Service's public IP, and deletes it when the Service is removed even if you set it toStatic. - Watch traffic drop on ingress-nginx. Scrape the
ingress-nginxrequest-rate metrics until requests reach zero. - Keep the DNS TTL low for a few hours of observation, then raise it back to your normal value.
Step 8: Clean up
After traffic is stable on the Gateway API path, stop the add-on from reconciling a default NginxIngressController on the cluster, then delete every existing NginxIngressController custom resource. The az aks approuting update --nginx None command in this step stops the controller from reconciling new resources, but the nginx Deployment and Service it created earlier remain on the cluster until you remove the owning NginxIngressController.
Update the cluster so the application routing add-on no longer manages a default ingress-nginx controller:
az aks approuting update --resource-group $RG --name $CLUSTER --nginx None
Then delete the remaining NginxIngressController resources:
kubectl delete nginxingresscontrollers.approuting.kubernetes.azure.com --all
Raise the DNS TTL back to its normal value once you verify steady state.
Roll back
Both data planes can coexist, so rollback is symmetric at any point before you disable the ingress-nginx add-on. This procedure leaves the original Ingress resources in place throughout the cutover, so a rollback only has to revert DNS:
If you removed the
Ingressresources after cutover, recreate them.Flip DNS back to
INGRESS_NGINX_IPand wait for the TTL window to drain.Optionally disable the Gateway API implementation:
az aks update --resource-group $RG --name $CLUSTER --disable-app-routing-istio
After you run kubectl delete nginxingresscontrollers.approuting.kubernetes.azure.com, the ingress-nginx controller is gone. Rolling back from that point requires re-enabling the add-on Default NginxIngressController and recreating any Ingress resources you removed. Validate the chosen path before you disable ingress-nginx.