The Kubernetes Gateway API is a specification for traffic management on Kubernetes clusters. It improves on the Ingress API with a role-oriented, provider-agnostic model for advanced routing, expressed through resources such as GatewayClass, Gateway, and HTTPRoute.
On AKS the Managed Gateway API installation installs and manages the standard-channel Gateway API Custom Resource Definitions (CRDs) on the cluster. It installs the CRDs only: it does not install any implementation and does not create a GatewayClass. An implementation that actually routes traffic must be deployed separately, exactly as on real AKS.
This tutorial enables the Managed Gateway API on an AKS cluster (or the LocalStack for Azure emulator), installs NGINX Gateway Fabric as the Gateway API implementation, and exposes an echo-server backend through a Gateway and an HTTPRoute. It then verifies, from the host, that a request for the route's hostname reaches the backend while a request for any other hostname does not, proving the gateway makes the routing decision. There is no cert-manager and no DNS: the hostname is local-only and reached with kubectl port-forward and an explicit Host header.
Running on LocalStack? Install the lstk CLI and run
lstk az start-interceptionto route Azure CLI calls to the emulator. See Run against LocalStack for the full setup.
- An AKS cluster reachable through
kubectl, created with scripts/01-user-assigned-managed-identity.sh (or the system-assigned variant). The values in 00-variables.sh (clusterlocal-aks-test, resource grouplocal-rg, locationItalyNorth) must match the cluster the script creates; edit them if you changed the cluster script'sprefix,suffix, orlocation. - Azure CLI (
az) version2.86.0or later, which is required for the--enable-gateway-apiflag. - kubectl configured for the cluster.
- Helm to install NGINX Gateway Fabric.
curland jq for the routing check.04-test-routing.shinstallsjqwithaptif it is missing on a Debian or Ubuntu host.
The cluster-creation scripts already enable the Managed Gateway API at create time, so 01-enable-gateway-api.sh is idempotent: on such a cluster it detects the installation and leaves it in place. It still runs cleanly on a cluster created without it, enabling it through the live az aks update path.
The Managed Gateway API add-on installs only the CRDs; NGINX Gateway Fabric is the implementation that claims the GatewayClass and serves the traffic:
%%{init: {'themeVariables': {'clusterBkg': 'transparent', 'clusterBorder': '#8c8c8c'}}}%%
flowchart LR
client(["curl -H 'Host: echo.local'"])
subgraph aks["AKS cluster"]
crds["gateway.networking.k8s.io CRDs<br/>installed by the add-on"]
gwclass["GatewayClass nginx"]
subgraph ngfns["namespace nginx-gateway"]
controller["NGINX Gateway Fabric<br/>controller"]
dataplane["per-Gateway NGINX<br/>data-plane Service and pods"]
end
subgraph testns["namespace gateway-api-test"]
gw["Gateway<br/>HTTP listener for echo.local"]
route["HTTPRoute<br/>every path of echo.local"]
svc["echo-server<br/>ClusterIP Service"]
deploy["echo-server<br/>Deployment"]
end
end
crds -.->|"define"| gw
gwclass -.->|"claimed by"| controller
gw -.->|"provisions"| dataplane
route -.->|"attached to"| gw
client -->|"kubectl port-forward"| dataplane
dataplane -->|"routes per HTTPRoute"| svc
svc --> deploy
Run the numbered scripts in order. Each one sources 00-variables.sh and is idempotent, so it can be re-run safely.
| Script | What it does | Expected result |
|---|---|---|
00-variables.sh |
Shared variables (cluster name and resource group, the gateway.networking.k8s.io CRD group, the NGINX Gateway Fabric release, and the echo-server names, hostname, and port). Sourced by every script, never run directly. |
Variables exported. |
01-enable-gateway-api.sh |
Enables the Managed Gateway API on the cluster (az aks update --enable-gateway-api), merges the cluster credentials into kubeconfig, and waits for the CRDs. |
The standard-channel CRDs (gatewayclasses, gateways, grpcroutes, httproutes, referencegrants) are installed; the bundle version and standard channel are printed. |
02-install-nginx-gateway-fabric.sh |
Installs NGINX Gateway Fabric via Helm from its OCI registry, with its front Service of type LoadBalancer. |
The nginx GatewayClass registered by the chart reaches Accepted. |
03-deploy-echoserver.sh |
Deploys the echoserver Deployment and ClusterIP Service, a Gateway on the nginx class with an HTTP listener for echo.local, and an HTTPRoute binding echo.local to the Service. |
The Gateway reaches Programmed and the Deployment reaches Available. |
04-test-routing.sh |
Waits for the NGINX Gateway Fabric data-plane pods, port-forwards the per-Gateway Service, then curls it with the claimed Host header and with an unclaimed one. |
echo.local returns HTTP 200; the unclaimed host does not; the echo-server response is printed. |
05-cleanup.sh |
Deletes the sample namespace and uninstalls NGINX Gateway Fabric. Pass --disable-gateway-api to also remove the Managed Gateway API CRDs. |
The sample resources are removed. |
cd gateway-api/scripts
./01-enable-gateway-api.sh
./02-install-nginx-gateway-fabric.sh
./03-deploy-echoserver.sh
./04-test-routing.sh
# ./05-cleanup.sh # when finished
# ./05-cleanup.sh --disable-gateway-api # also remove the Gateway API CRDs00-variables.sh: Defines the shared variables sourced by every other script: the target cluster (local-aks-test) and resource group (local-rg), thegateway.networking.k8s.ioCRD group, the NGINX Gateway Fabric namespace, release name, OCI chart, andnginxGatewayClass, and the echo-server namespace, workload names,echo.localhostname, container image, and port.01-enable-gateway-api.sh: Confirms the cluster exists, enables the Managed Gateway API withaz aks update --enable-gateway-api(skipping the update wheningressProfile.gatewayApi.installationis alreadyStandard), merges the cluster credentials intokubeconfig, and polls until the standard-channel CRDs appear. It then lists the CRDs and the Gateway API resources (kubectl api-resources) and prints thegateway.networking.k8s.io/bundle-versionandgateway.networking.k8s.io/channelannotations the managed installation stamps on them.02-install-nginx-gateway-fabric.sh: Verifies the CRDs are present, thenhelm installs NGINX Gateway Fabric fromoci://ghcr.io/nginx/charts/nginx-gateway-fabric(unpinned, latest) withnginx.service.type=LoadBalancer, and waits for thenginxGatewayClassto beAccepted. The Managed Gateway API installs only the CRDs, so this bring-your-own implementation is what actually serves traffic.03-deploy-echoserver.sh: Verifies thenginxGatewayClassexists, creates thegateway-api-testnamespace, and applies the echo-serverDeploymentandClusterIPService, aGatewayon thenginxclass with an HTTP listener forecho.local, and anHTTPRoutethat routes every path ofecho.localto the Service. It waits for theGatewayto beProgrammedand theDeploymentto beAvailable.04-test-routing.sh: Discovers the per-Gatewaydata-planeServiceNGINX Gateway Fabric provisions (by thegateway.networking.k8s.io/gateway-namelabel), waits for its pods to beReady, andkubectl port-forwards it to a local port. It asserts that a request withHost: echo.localreturns HTTP200and that a request for an unclaimed host does not reach the backend, then prints a full echo-server response. TheLoadBalancerexternal IP is synthetic on the emulator, so the data path is exercised through the port-forward.05-cleanup.sh: Deletes thegateway-api-testnamespace (removing theDeployment,Service,Gateway, andHTTPRoute) and uninstalls the NGINX Gateway Fabric release and its namespace. When passed--disable-gateway-api, it also runsaz aks update --disable-gateway-api, which removes the managed CRDs and, with them, any remaining Gateway API resources.
The Kubernetes manifests (the Deployment, Service, Gateway, and HTTPRoute) are defined inline in 03-deploy-echoserver.sh and templated from the variables in 00-variables.sh, so there are no separate YAML files.