Choose a supported replacement by routing behaviour, engineering effort and overlap cost—not by whether the YAML converts.

Your AKS nginx ingress migration starts with a replacement decision, not a manifest converter. Microsoft’s critical-security-patch support for the managed application-routing NGINX add-on runs through November 2026, but the recommended Gateway API replacement cannot preserve every NGINX behaviour. Rate limits, Lua and request-size controls can change the answer. Use a behaviour-first scorecard to choose a supported platform, estimate the engineering work and overlap budget, then prove TLS, authentication and rollback before moving production traffic.
Which replacement preserves your application’s behaviour?
Find the annotations and IP dependencies that decide the work
Map annotations to capabilities before choosing the default
Build a parallel gateway and prove the customer journeys
Price the overlap and the annotation remediation separately
Cut over only while DNS and IP rollback still work
Leave with a migration decision your team can execute
| Buyer scorecard | Application-routing Gateway API | Application Gateway for Containers | Gateway API ingress through AKS Istio |
|---|---|---|---|
| Ordinary HTTP/HTTPS routing | Sensible starting candidate | Azure-managed alternative | Managed mesh ingress path |
| Custom policy requirements | Check supported policies | Map requirements to supported capabilities | Check policies; custom extensions have separate support boundaries |
| Data-plane ownership | Runs in your AKS cluster | Traffic processing outside AKS | Runs in your AKS cluster |
| Upgrade responsibility | Microsoft-managed add-on; you validate application behaviour | Azure-managed service; you validate configuration changes | Microsoft-managed mesh; you plan revision upgrades |
| Azure integration | AKS application-routing add-on | Azure gateway resource and Kubernetes integration | AKS Istio service mesh add-on |
| Commercial fit | Compare cluster and load-balancer costs | Price gateway, capacity and overlap | Include gateway resources and mesh operations |
Use Cloudline’s Behaviour, Ownership, Budget and Exit framework for your AKS nginx ingress migration. Mandatory behaviour is a veto: broken authentication, redirects or timeouts disqualify a candidate. Ownership and budget rank only those that pass. Exit means retaining a workable rollback, not merely exporting YAML.
Application-routing Gateway API is the starting candidate for straightforward ingress, not an automatic winner. Application Gateway for Containers cost needs its own calculation. Neither managed branding nor Gateway API compatibility guarantees nginx annotation migration without application changes.
Planning date: 30 October 2026. Upstream ingress-nginx maintenance ended in March 2026; Microsoft’s managed-add-on security-patch bridge runs through November 2026. These are different deadlines. Target internal cutover by 6 November, leaving at least two weeks for observation and recovery while DNS and IP rollback remain usable.
This decision spans application behaviour, platform operations and spend—the scope of Cloudline’s Kubernetes and cloud services.
A replacement that drops a mandatory control is not the cheaper option.
Share this quote

Start the AKS nginx ingress migration with a live inventory, not the default controller name. Set the production resource names and confirm your Kubernetes context before collecting:
RG='rg-saas-prod-uksouth'
CLUSTER='aks-saas-prod-uksouth'
az aks show -g "$RG" -n "$CLUSTER" --query '{version:kubernetesVersion,appRouting:ingressProfile.webAppRouting,nodeRG:nodeResourceGroup}' -o json
kubectl get nginxingresscontrollers -o custom-columns=NAME:.metadata.name,CLASS:.spec.ingressClassName
kubectl get ingress -A -o wide
kubectl get svc -n app-routing-system -o wide
kubectl get ingress -A -o json | jq -r '.items[] | . as $i | (.metadata.annotations // {} | to_entries[]) | select(.key | startswith("nginx.ingress.kubernetes.io/")) | [$i.metadata.namespace,$i.metadata.name,($i.spec.ingressClassName // $i.metadata.annotations["kubernetes.io/ingress.class"] // ""),.key,.value] | @tsv'
Inventory every discovered class, not just webapprouting.kubernetes.azure.com. Include legacy class annotations and classless Ingresses. Microsoft’s migration procedure explicitly covers multiple controllers and their separate public addresses.
The TSV export produces an audit input, not a compatibility verdict for nginx annotation migration. Inspect controller ConfigMaps, snippets and Helm/GitOps definitions too: annotations alone are not the estate.
| Inventory unit | Record |
|---|---|
| Each route | Hostname, path, namespace, Service and port, controller class |
| Security | Certificate source and renewal owner; authentication mechanism |
| Accountability | Owner and acceptance test, including expected redirects and failures |
| Each controller | Service, public IP, Azure resource ID and configuration source |
Trace the dependency chain: client → DNS or Front Door → controller public IP → route → Service → pod. Add Traffic Manager, customer allowlists, webhook destinations and fixed-IP consumers. Record who controls each dependency and how long changes take; a healthy gateway does not prove customers can reach it.
Where pre-existing Azure resources need controlled infrastructure ownership, use Terraform import into existing modules alongside reviewed configuration and plans. Import alone does not make ownership changes safe.

For nginx annotation migration, map required behaviour—not annotation names. Native means a documented capability, not identical semantics. Redesign changes the architecture; Custom extension adds team-owned configuration; Blocker means the required control is unavailable in that implementation.
| Legacy behaviour | Application-routing Gateway API | Application Gateway for Containers | Istio ingress | Support boundary | Required test |
|---|---|---|---|---|---|
rewrite-target, use-regex |
Native: prefix; Redesign: captures | Native: documented rewrites; Redesign: captures | Native: routing; Redesign: captures | No automatic NGINX translation | Captures, query strings, trailing slashes |
| HTTPS redirects | Native | Native | Native | Configure redirect and TLS separately | Status, hostname, port, query preservation |
auth-url, auth-signin |
Redesign | Redesign | Custom extension | Own the authentication integration | Login, callback, cookies, logout |
proxy-body-size |
Blocker | Redesign | Custom extension | Managed default lacks configurable limits | Uploads immediately below/above boundary |
| Header-size controls | Blocker | Redesign | Custom extension | Platform ceilings are not configurable parity | Large cookies; oversized-header rejection |
limit-rps, limit-connections |
Blocker | Redesign | Custom extension | Own throttle semantics and dependencies | Burst, concurrency, replicas, client identity |
| Snippets/Lua-dependent behaviour | Blocker | Redesign | Custom extension | No portable snippet/Lua contract | Every programmable branch |
A standard prefix rewrite proves neither regex captures nor trailing-slash behaviour. Authentication needs an explicit destination architecture: application middleware, an authentication proxy or a supported gateway integration—not copied annotations.
The managed migration guide identifies unavailable configurable body/header limits, Lua and local/global rate limiting. EnvoyFilter is not an escape hatch for application routing Gateway API.
Application Gateway for Containers documents routing, TLS and WAF capabilities. Neither Application Gateway v2 features nor arbitrary NGINX annotations establish compatibility; external authentication, exact throttling and programmable Lua require redesign.
Istio permits gateway-scoped customisation, but Azure excludes problems caused by custom EnvoyFilter configuration. The specific Istio Gateway API ingress guide describes ingress without requiring workload sidecars.
Do not choose the default while a mandatory control lacks a supported implementation and accountable owner. Hypothetically, /health passes while login redirects loop or an upload boundary changes. Like our cgroup v2 go/no-go gates, this is an explicit compatibility decision—not shared implementation guidance.
Converted YAML is a starting point, not evidence of equivalent behaviour.
Share this quote

Build this parallel gateway only if the application-routing candidate passed your scorecard. This AKS nginx ingress migration example is not a generic replacement for every controller.
Require Azure CLI 2.86.0 or later and AKS-managed Gateway API CRDs; do not install upstream CRDs separately. The edge namespace must already contain TLS Secret api-tls and Service api, exposing port 8080.
az aks update -g "$RG" -n "$CLUSTER" --enable-gateway-api --enable-app-routing-istio
Apply this baseline alongside NGINX. It does not recreate authentication, rate limits or body limits.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public-gateway
namespace: edge
spec:
gatewayClassName: approuting-istio
listeners:
- name: https
hostname: api.example.co.uk
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- group: ""
kind: Secret
name: api-tls
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api
namespace: edge
spec:
parentRefs:
- name: public-gateway
sectionName: https
hostnames:
- api.example.co.uk
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api
port: 8080
For managed Key Vault certificates, use the documented listener TLS options and workload identity configuration. The application-routing operator synchronises certificates from Key Vault. Test rotation rather than assuming it works.
Check Gateway Programmed=True and the route’s matching parent conditions: Accepted=True and ResolvedRefs=True. Confirm they reflect the current configuration.
kubectl get gateway public-gateway -n edge -o yaml
kubectl get httproute api -n edge -o yaml
HOST=api.example.co.uk
GW_IP=$(kubectl get gateway public-gateway -n edge \
-o jsonpath='{.status.addresses[0].value}')
curl --resolve "${HOST}:443:${GW_IP}" "https://${HOST}/api/health"
openssl s_client -connect "${GW_IP}:443" -servername "$HOST" -verify_hostname "$HOST" -verify_return_error </dev/null
Keep production DNS publication outside this parallel test. Use controlled client overrides for browser journeys too, including callback hosts. Never hide certificate failures with -k.
Define expected outcomes from the old contract: status, headers, redirects, cookies and connection behaviour—not universal 200 responses. Use backend readiness and draining checks for workload continuity; they do not replace controller migration tests.

Price infrastructure overlap separately from nginx annotation migration. Cheap gateway hours do not make incompatible behaviours cheap to replace.
Use one production cluster in UK South, with 14 days (336 hours) of overlap: one non-WAF Application Gateway for Containers, one frontend and one association. Assume five average capacity units—not a sizing recommendation. Capacity considers connections and throughput; five units are not a universal request-rate allowance.
Retrieve GBP retail rates:
curl -fsSG 'https://prices.azure.com/api/retail/prices' --data-urlencode "currencyCode='GBP'" --data-urlencode "\$filter=armRegionName eq 'uksouth' and serviceName eq 'Application Gateway' and priceType eq 'Consumption'" | jq '.Items[] | select(.productName | contains("Containers")) | {productName,skuName,meterName,retailPrice,unitOfMeasure,effectiveStartDate}'
Follow every NextPageLink until empty. Explicitly select non-WAF meters. Archive raw pages: this projection omits meterId. GBP API values are budgeting references, not contracted invoice quotes.
| Worksheet entry | Record or calculate |
|---|---|
| Evidence | Retrieval date, meter IDs, effective dates and units |
| Four separate hourly rates (£) | G: gateway; F: frontend; A: association; C: capacity unit |
| New AGC overlap | 336 × (G + F + A + 5C) |
| 730-hour steady state | 730 × (G + F + A + 5C) |
Keep existing baseline and incremental migration spend in separate columns. Use baseline tagging and spend ownership; never charge unchanged production traffic twice. Itemise:
Cloudline’s illustrative model: three engineer-days for inventory and platform setup; half a day per distinct simple behaviour family; two days per incompatible family; two days for acceptance and cutover preparation.
Four simple families and two incompatible families total 11 engineer-days. Add 25% planning contingency: 13.75, approximately 14 days of effort. Two engineers do not automatically mean seven elapsed days: dependencies, review and acceptance constrain parallel work.

For an AKS nginx ingress migration, every gate needs evidence and a named owner. Any failed gate means no-go.
| Gate | Evidence | Owner | Rollback action |
|---|---|---|---|
| Behaviour accepted | All journeys pass; current-generation Gateway Accepted/Programmed and route Accepted/ResolvedRefs conditions are True. | Application lead | Keep old path serving. |
| Old endpoint survives | Independently tested; old DNS and origin configuration recorded. | Platform lead | Restore recorded configuration. |
| TTL reduced | Previous TTL’s cache window has expired since reduction. | DNS owner | Delay cutover; retain old endpoint. |
| Dependencies reversible | Front Door origins, Traffic Manager endpoints, customer allowlists and certificate/DNS automation have named owners and recorded configurations. | Change lead | Restore every changed dependency. |
The application-routing and Istio service mesh Gateway API implementations cannot coexist on one cluster. NGINX can remain alongside either selected target. Comparing these two Istio-based options therefore needs separate clusters or sequential experiments.
Agree estate-specific abort thresholds. Suggested triggers: any authentication bypass, a failed critical upload journey, or five minutes of 5xx exceeding baseline by one percentage point. Agree numerical latency and business-transaction thresholds too.
On abort, restore DNS and every changed origin dependency, then test both paths during cache drain. DNS reversal is not instantaneous; persistent connections can outlast the TTL. Do not promise exact convergence.
Do not move the old controller’s cloud-managed public IP to the new Service. Deleting its owning Service can remove the address even when labelled Static. Keep separate addresses throughout rollback eligibility.
Before deleting controllers, complete an agreed observation period and require negligible old-path traffic, with every residual caller understood. Controller deletion is the point of no return: it ends the simple DNS-only rollback path.

Your AKS nginx ingress migration should end with a signed retirement decision, not “the new endpoint responds”. A healthy gateway does not prove that customer behaviour survives or that recovery works.
Choose one outcome: ready to schedule; ready after named remediation, with owners and dates; or not ready, because a critical behaviour or recovery path remains unresolved. Only the first authorises scheduling controller retirement.
Cloudline’s migration-readiness assessment can produce an annotation/capability map, target recommendation, effort range, overlap budget and go/no-go checklist. Bring sanitised Ingress exports and the names of your critical customer journeys—never private keys or production credentials. Talk to a cloud engineer about migration readiness.
The right replacement is the one that preserves your required behaviour within a support and ownership model your team can sustain. Make that decision before spending the remaining migration window converting manifests. Inventory the estate, resolve incompatible controls, budget the overlap and rehearse recovery while the old endpoint still works. Cloudline Consulting can help turn sanitised ingress configuration and critical customer journeys into a migration-readiness assessment with a clear target, engineering estimate and evidence-led cutover decision.