NGINX Gateway Fabric


NGINX Gateway Fabric is a Kubernetes Gateway API controller that uses NGINX to handle incoming traffic. You can use it to publish HTTP, HTTPS, gRPC, TCP, and UDP services, configure routing by domain and path, manage TLS, and gradually transition from Ingress to the Gateway API.

Gateway API is the evolution of Ingress. Instead of a single Ingress resource, it splits responsibilities across several dedicated objects:

  • GatewayClass defines which controller will manage the gateways.
  • Gateway describes the entry point: ports, protocols, domains, and TLS settings.
  • HTTPRoute defines HTTP traffic routing rules to backend services.
  • GRPCRoute, TCPRoute, UDPRoute, TLSRoute handle other traffic types.

NGINX Gateway Fabric watches Gateway API resources and uses them to create an NGINX data plane: NGINX pods and a service through which external traffic enters the cluster and is routed to Kubernetes services.

Installation
Copy link

Before installing NGINX Gateway Fabric from the dashboard, you need to manually install the Gateway API CRDs.

Specify the NGINX Gateway Fabric version in the ref parameter. For example, for version v2.6.3:

kubectl kustomize "https://github.com/nginx/nginx-gateway-fabric/config/crd/gateway-api/standard?ref=v2.6.3" | kubectl apply -f -

After installing the CRDs:

  1. Go to the Kubernetes section and click on the cluster.
  2. Navigate to the Addons tab and select NGINX Gateway Fabric.
  3. (Optional) Enable Advanced setup to edit values.yaml. For most use cases, the defaults are a good starting point.
  4. Click Install.
  5. Wait for the process to complete and verify that the NGINX Gateway Fabric pods are running:
kubectl get pods -n nginx-gateway

All pods should show a Running status.

You can also confirm that the Gateway API resources are available in the cluster:

kubectl api-resources | grep gateway.networking.k8s.io

A standard installation of NGINX Gateway Fabric creates a GatewayClass named nginx. Check its status:

kubectl get gatewayclass nginx

The ACCEPTED column should show True.

Usage Example
Copy link

This example shows how to configure NGINX Gateway Fabric to accept HTTP traffic on a single external IP and route requests to two different services:

  • http://app.example.com/service1 routes to service1
  • http://app.example.com/service2 routes to service2

Start by creating a dedicated namespace:

kubectl create namespace nginx-gateway-example

Create a ConfigMap
Copy link

Create a file called configmap.yaml with HTML pages for both services:

apiVersion: v1
kind: ConfigMap
metadata:
 name: nginx-pages
 namespace: nginx-gateway-example
data:
 service1.html: |
   <html>
     <head><title>Service 1</title></head>
     <body><h1>Service 1</h1></body>
   </html>
 service2.html: |
   <html>
     <head><title>Service 2</title></head>
     <body><h1>Service 2</h1></body>
   </html>

Apply the manifest:

kubectl apply -f configmap.yaml

Create Deployments and Services
Copy link

Create service1.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: service1
 namespace: nginx-gateway-example
spec:
 replicas: 2
 selector:
   matchLabels:
     app: service1
 template:
   metadata:
     labels:
       app: service1
   spec:
     containers:
       - name: nginx
         image: nginx:latest
         ports:
           - containerPort: 80
         volumeMounts:
           - name: html
             mountPath: /usr/share/nginx/html
     volumes:
       - name: html
         configMap:
           name: nginx-pages
           items:
             - key: service1.html
               path: index.html
---
apiVersion: v1
kind: Service
metadata:
 name: service1
 namespace: nginx-gateway-example
spec:
 selector:
   app: service1
 ports:
   - name: http
     port: 80
     targetPort: 80
 type: ClusterIP

Create service2.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: service2
 namespace: nginx-gateway-example
spec:
 replicas: 2
 selector:
   matchLabels:
     app: service2
 template:
   metadata:
     labels:
       app: service2
   spec:
     containers:
       - name: nginx
         image: nginx:latest
         ports:
           - containerPort: 80
         volumeMounts:
           - name: html
             mountPath: /usr/share/nginx/html
     volumes:
       - name: html
         configMap:
           name: nginx-pages
           items:
             - key: service2.html
               path: index.html
---
apiVersion: v1
kind: Service
metadata:
 name: service2
 namespace: nginx-gateway-example
spec:
 selector:
   app: service2
 ports:
   - name: http
     port: 80
     targetPort: 80
 type: ClusterIP

Apply both manifests:

kubectl apply -f service1.yaml
kubectl apply -f service2.yaml

Verify the pods are running:

kubectl get pods -n nginx-gateway-example

Create a Gateway
Copy link

The Gateway resource defines the external entry point for your application. With a standard NGINX Gateway Fabric installation, it manages Gateways that use gatewayClassName: nginx.

Create gateway.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
 name: app-gateway
 namespace: nginx-gateway-example
spec:
 gatewayClassName: nginx
 listeners:
   - name: http
     hostname: app.example.com
     protocol: HTTP
     port: 80
     allowedRoutes:
       namespaces:
         from: Same

Apply it:

kubectl apply -f gateway.yaml

Once the Gateway is created, NGINX Gateway Fabric will deploy an NGINX data plane and a load balancer in the nginx-gateway-example namespace. Check the status with:

kubectl get gateway app-gateway -n nginx-gateway-example

Wait until PROGRAMMED shows True and ADDRESS shows an external IP.

You can also inspect the resources that were created:

kubectl get deployments -n nginx-gateway-example
kubectl get svc -n nginx-gateway-example

Create an HTTPRoute
Copy link

HTTPRoute defines the routing rules.

Create httproute.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
 name: app-route
 namespace: nginx-gateway-example
spec:
 parentRefs:
   - name: app-gateway
 hostnames:
   - app.example.com
 rules:
   - matches:
       - path:
           type: PathPrefix
           value: /service1
     filters:
       - type: URLRewrite
         urlRewrite:
           path:
             type: ReplacePrefixMatch
             replacePrefixMatch: /
     backendRefs:
       - name: service1
         port: 80
   - matches:
       - path:
           type: PathPrefix
           value: /service2
     filters:
       - type: URLRewrite
         urlRewrite:
           path:
             type: ReplacePrefixMatch
             replacePrefixMatch: /
     backendRefs:
       - name: service2
         port: 80

Apply it:

kubectl apply -f httproute.yaml

Confirm the route is accepted:

kubectl get httproute app-route -n nginx-gateway-example -o yaml

The resource status should include both conditions:

status: "True"
type: Accepted
status: "True"
type: ResolvedRefs

This HTTPRoute accepts requests for app.example.com and routes them to different services based on the path prefix. The URLRewrite filter strips the /service1 and /service2 prefixes, replacing them with /, so NGINX inside each service serves its main page correctly.

Verify the Setup
Copy link

Add the load balancer IP as an A record for app.example.com in your DNS settings. Once DNS propagates, test both endpoints:

curl http://app.example.com/service1
curl http://app.example.com/service2

If DNS hasn't propagated yet, you can test by IP using the Host header:

curl -H "Host: app.example.com" http://<EXTERNAL_IP>/service1
curl -H "Host: app.example.com" http://<EXTERNAL_IP>/service2

Both requests should return the respective Service 1 and Service 2 pages.

Clean Up
Copy link

To remove all resources created in this example, run:

kubectl delete namespace nginx-gateway-example

This does not uninstall the NGINX Gateway Fabric add-on. To remove it, go to the Addons tab in the cluster dashboard.

Migrating from Nginx Ingress to NGINX Gateway Fabric
Copy link

You can run NGINX Gateway Fabric alongside Nginx Ingress and migrate routes gradually. This lets you test Gateway API resources on a separate external IP before switching DNS records or redirecting traffic to the new Gateway.

To automate the conversion of existing Ingress resources, use the ingress2gateway utility. It reads Ingress resources from your cluster or from YAML files and outputs equivalent Gateway API resources: Gateway, HTTPRoute, and related objects.

Prerequisites
Copy link

Before migrating, make sure your cluster is ready:

  • Nginx Ingress and NGINX Gateway Fabric are both installed.
  • Existing Ingress resources are working correctly through Nginx Ingress.
  • A GatewayClass named nginx exists in the cluster.

If Nginx Ingress was installed from the Hostman dashboard, it uses an IngressClass named nginx. You will need this name when running ingress2gateway.

Check that both classes are present:

kubectl get ingressclass
kubectl get gatewayclass

The output should include an IngressClass named nginx and a GatewayClass named nginx.

Install ingress2gateway
Copy link

If you have Go installed locally, run:

go install github.com/kubernetes-sigs/ingress2gateway@v1.1.0

The binary will be placed in $(go env GOPATH)/bin. Make sure this directory is in your PATH.

Alternatively, install via Homebrew:

brew install ingress2gateway

Or download a prebuilt binary from the project releases page.

Source Setup
Copy link

In this example, the migration targets an application already published through Nginx Ingress.

The cluster has a namespace called ingress-migration-demo containing:

  • An Ingress resource named migration-source
  • Services service1 and service2
  • Two routing rules for the domain app.example.com:
    • /service1 routes to service1
    • /service2 routes to service2

Verify the source Ingress:

kubectl get ingress -n ingress-migration-demo

The CLASS column should show nginx.

Convert Ingress Resources
Copy link

To convert the Ingress to Gateway API resources, run:

ingress2gateway print \
  --providers=ingress-nginx \
  --ingress-nginx-ingress-class=nginx \
  --namespace=ingress-migration-demo > gateway-migration.yaml

The flags in this command:

  • print outputs the generated resources to stdout; the > redirect saves them to gateway-migration.yaml.
  • --providers=ingress-nginx specifies the source Ingress controller type.
  • --ingress-nginx-ingress-class=nginx filters for Ingress resources using the nginx class.
  • --namespace=ingress-migration-demo scopes the conversion to the source namespace.

Review and Fix the Output
Copy link

Open gateway-migration.yaml and review the generated resources before applying them.

Check the gatewayClassName field in the Gateway resource. For a standard NGINX Gateway Fabric installation, the value should be nginx:

spec:
  gatewayClassName: nginx

Also review the HTTPRoute rules. Some Ingress annotations may be converted into filters or match types that need manual adjustment. For example, for standard path prefix routing, use PathPrefix:

rules:
 - matches:
     - path:
         type: PathPrefix
         value: /service1
   filters:
     - type: URLRewrite
       urlRewrite:
         path:
           type: ReplacePrefixMatch
           replacePrefixMatch: /
   backendRefs:
     - name: service1
       port: 80

If the generated file contains RegularExpression routes or unsupported annotations, check their compatibility with NGINX Gateway Fabric and replace them with supported Gateway API rules.

Apply the Gateway API Resources
Copy link

Once you're satisfied with the output, apply it:

kubectl apply -f gateway-migration.yaml

Verify the Gateway has been created and has received an external IP:

kubectl get gateway -A

Check that the HTTPRoute has been accepted by the controller:

kubectl get httproute -A
kubectl describe httproute <route-name> -n <namespace>

The HTTPRoute status should show both Accepted=True and ResolvedRefs=True.

Test Before Switching Traffic
Copy link

While your DNS records still point to the old Nginx Ingress, verify the new Gateway by sending requests directly to its external IP with a Host header:

curl -H "Host: app.example.com" http://<EXTERNAL_IP>/<path>

Where:

  • app.example.com is the domain from your HTTPRoute.
  • <EXTERNAL_IP> is the IP address of the new Gateway.
  • <path> is the path handled by the route.

Compare the responses from the old Ingress and the new Gateway. If everything looks correct, update your DNS records to point to the new load balancer's IP.