From ingress-nginx to AKS App Routing: A Phased Migration Guide

From ingress-nginx to AKS App Routing: A Phased Migration Guide

Learn how to migrate from the community ingress-nginx controller to Microsoft's AKS App Routing add-on with minimal downtime using a phased approach that runs both controllers side by side.

· 5 min read

With the community version of the ingress-nginx controller reaching end-of-life in March 2026 (as announced by the Kubernetes project), it’s an opportune time to migrate to the Microsoft-managed AKS App Routing add-on. This first-party solution is fully integrated with AKS and removes maintenance overhead from your operations team.

In this article, I’ll walk you through a phased migration approach that runs both controllers side by side temporarily, allowing you to migrate applications one by one with minimal downtime.

Alternatives to Consider

While this article focuses on the App Routing add-on, it’s worth noting that other alternatives exist, including:

  • Application Gateway for Containers
  • Envoy Gateway
  • Traefik Proxy

However, for AKS users, the App Routing add-on is generally the easiest to implement, offering straightforward integration with minimal configuration overhead..

Migration Strategy: Parallel Controllers with Incremental Migration

The core principle of this migration is enabling the App Routing add-on alongside your existing ingress-nginx controller. Each controller gets its own external IP address, allowing you to migrate applications individually by updating ingress manifests and DNS records.

Step 1: Preparation

First, note your current inbound IP address:

kubectl get svc -n ingress-nginx

Next, lower the TTL of all relevant DNS records at your DNS provider to 300 seconds. This significantly speeds up DNS propagation during the migration.

Important: If you use wildcard DNS records (like *.example.com), create separate entries for each ingress before starting. Switching a wildcard moves all applications to the new IP simultaneously, preventing per-application validation.

Finally, coordinate with your team to freeze deployments on applications in the cluster during the migration to avoid conflicts between old and new ingressClassName values.

Step 2: Enable the App Routing Add-on

Enable the add-on for your AKS cluster:

az aks approuting enable --resource-group <rg> --name <aks-cluster>

Note the new IP address that will be your migration target:

kubectl get svc -n app-routing-system nginx

Step 3: Update Repository Manifests

Update your ingress manifests in your repository (but don’t deploy yet). Remove the old annotation:

annotations:
  kubernetes.io/ingress.class: nginx   # <-- remove this

And add the new ingressClassName under the spec section:

spec:
  ingressClassName: webapprouting.kubernetes.azure.com

Step 3b: Inventory Custom Annotations

Check which custom annotations are present across your ingress objects:

kubectl get ingress -A -o yaml | grep -A5 annotations

Common nginx-specific annotations include rate limiting, rewrites, CORS, and authentication. Not all have direct equivalents in App Routing, so plan alternative solutions where needed.

Certificate Expiry Check

Before proceeding, check when your certificates expire to plan your migration window:

kubectl get certificates -A -o json | ConvertFrom-Json | 
  Select-Object -ExpandProperty items | 
  ForEach-Object {
    [PSCustomObject]@{
      Namespace = $_.metadata.namespace
      Name      = $_.metadata.name
      Ready     = $_.status.conditions[0].status
      Expiry    = $_.status.notAfter
    }
  } | Sort-Object Expiry | Format-Table -AutoSize

Or via TLS secrets (if you don’t want to query cert-manager)

kubectl get secrets -A -o json | ConvertFrom-Json | 
  Select-Object -ExpandProperty items | 
  Where-Object { $_.type -eq "kubernetes.io/tls" } |
  ForEach-Object {
    $certData = [System.Convert]::FromBase64String($_.data.'tls.crt')
    $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($certData)
    [PSCustomObject]@{
      Namespace = $_.metadata.namespace
      Secret    = $_.metadata.name
      Expiry    = $cert.NotAfter
      DaysLeft  = ($cert.NotAfter - (Get-Date)).Days
    }
  } | Sort-Object DaysLeft | Format-Table -AutoSize

Step 4: Phased Migration per Application

Repeat these steps for each application:

4.1 Update the Ingress Object

kubectl patch ingress <name> -n <namespace> \
  --type merge \
  -p '{"spec":{"ingressClassName":"webapprouting.kubernetes.azure.com"}}'

kubectl annotate ingress <name> -n <namespace> \
  kubernetes.io/ingress.class-

4.2 Verify Reachability

Test the application through the new IP:

curl -v -H "Host: your-app.example.com" http://<NEW-EXTERNAL-IP>/

4.3 Update DNS Records

Change the A record for your application from the old ingress-nginx IP to the new App Routing IP at your DNS provider.

4.4 Verify DNS Propagation

nslookup <hostname>

4.5 Validate the Application

curl https://<hostname>

Only proceed to the next application after successful validation.

Step 5: Sync Repositories and Update ClusterIssuer

After validating all applications, run your deployment pipelines to sync repositories with the cluster state.

Update the ClusterIssuer

For automatic certificate renewal via cert-manager, update your ClusterIssuer configuration:

Old configuration:

solvers:
  - http01:
      ingress:
        class: nginx

New configuration:

solvers:
  - http01:
      ingress:
        class: webapprouting.kubernetes.azure.com

Existing certificates remain active until expiry, and cert-manager will use the new configuration for renewals.

Step 6: Clean Up

Remove the old ingress-nginx installation:

helm uninstall ingress-nginx -n ingress-nginx

Don’t forget to remove ingress-nginx configuration from your Infrastructure as Code (Bicep templates, Helm values, pipeline steps, etc.).

Risks and Considerations

  • New IP Address: App Routing requires a new external IP, necessitating DNS changes for every application
  • DNS TTL: Lower TTL values well in advance (at least one full TTL period before migration)
  • Wildcard Records: Convert wildcards to individual records before starting to enable per-app validation
  • Custom Annotations: Inventory nginx-specific annotations beforehand as some lack direct equivalents
  • Deployment Freeze: Coordinate to prevent CI/CD runs during migration for affected applications

Conclusion

This phased migration approach minimizes downtime and risk by allowing incremental validation of each application. While the migration requires careful coordination and planning, the result is a more maintainable, Microsoft-supported ingress solution that’s fully integrated with your AKS cluster.

The key to success is thorough preparation, particularly around DNS TTL management and annotation compatibility. Take your time with each application migration and validate thoroughly before proceeding to the next one.