Kubernetes Gateway API on Runtime Fabric

Runtime Fabric (RTF) deploys Mule applications as Kubernetes pods inside worker namespaces. External traffic traditionally reached those pods through an Nginx Ingress Controller, but the Kubernetes Gateway API is now the CNCF standard and a better fit for RTF clusters: it handles cross-namespace routing natively, requires no vendor annotations, and separates cluster-operator concerns from application-developer concerns.

This guide assumes RTF is already installed and registered in Anypoint Platform. For RTF installation see Runtime Fabric Setup, and for MCP-based lifecycle management see Managing Runtime Fabric with the Anypoint MCP.

How RTF structures Kubernetes namespaces?

Understanding RTF’s namespace layout is essential before configuring routing.

graph TD
  subgraph cluster [Kubernetes Cluster]
    subgraph rtfNs [rtf namespace - RTF agent]
      agent[RTF Agent pods]
    end
    subgraph env1 [env-abc123 - Mule worker namespace]
      app1[order-api pod :8081]
      app2[customer-api pod :8081]
    end
    subgraph gw [gateway namespace - Nginx Gateway Fabric]
      ngf[NGF controller]
      svc[LoadBalancer Service]
    end
  end
  svc -->|HTTPRoute| app1
  svc -->|HTTPRoute| app2

RTF creates one worker namespace per Anypoint environment. Each namespace carries these labels applied by the RTF agent:

Label Value
rtf.mulesoft.com/agentNamespace The RTF agent namespace (e.g. rtf)
rtf.mulesoft.com/envId Anypoint environment ID
rtf.mulesoft.com/org Anypoint organisation ID
rtf.mulesoft.com/role Always workers for application namespaces

Mule applications listen on port 8081 by default for HTTP traffic. The Kubernetes Service created for each app exposes this port cluster-internally.

Why the Gateway API replaces Nginx Ingress on RTF?

Nginx Ingress on RTF Kubernetes Gateway API
kubernetes.io/ingress.class: nginx annotation per resource GatewayClass selected once at the Gateway level
No cross-namespace routing Native cross-namespace via ReferenceGrant
One Ingress resource spans hosts and paths Separate HTTPRoute per application, owned by the app team
Routing changes require cluster-level Ingress edits App teams update only their own HTTPRoute
nginx.ingress.kubernetes.io/* annotations for timeouts, CORS, rate limits Typed fields on HTTPRoute filters or policy CRDs

Prerequisites

  • RTF installed and at least one Mule application deployed
  • kubectl access to the RTF cluster with cluster-admin rights
  • Helm 3
  • Anypoint Platform access to retrieve environment IDs and application names (or the Anypoint MCP connected to Cursor)

Step 1: Install the Gateway API CRDs

Install the standard channel CRD bundle onto the RTF cluster:

1
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml

Verify:

1
kubectl get crd | grep gateway.networking.k8s.io

Expected output includes gatewayclasses, gateways, httproutes, and referencegrants.

Do not skip CRD installation even if Nginx Ingress Controller is already running. The Gateway API CRDs are a separate, independent API group and do not conflict with the existing Ingress API.

Step 2: Install Nginx Gateway Fabric

Nginx Gateway Fabric is the direct, Gateway API-native successor to Nginx Ingress Controller from the same vendor. It replaces the controller while keeping NGINX as the underlying proxy.

1
2
3
4
helm install ngf oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric \
  --namespace nginx-gateway \
  --create-namespace \
  --set service.type=LoadBalancer

Wait for the controller to be ready:

1
kubectl -n nginx-gateway rollout status deployment/ngf-nginx-gateway-fabric

Note the external IP: this is the cluster entry point that will replace the Nginx Ingress Controller’s LoadBalancer:

1
kubectl -n nginx-gateway get service ngf-nginx-gateway-fabric

Update your DNS records to point your API hostnames at this new IP before cutting traffic over.

Nginx Ingress Controller and Nginx Gateway Fabric can run simultaneously on the same cluster during migration. They use separate API groups (networking.k8s.io/v1 vs gateway.networking.k8s.io/v1) and do not interfere with each other.

Step 3: Create a GatewayClass

1
2
3
4
5
6
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: nginx
spec:
  controllerName: gateway.nginx.org/nginx-gateway-controller
1
2
kubectl apply -f gatewayclass.yaml
kubectl get gatewayclass nginx

ACCEPTED must show True before proceeding.

Step 4: Create a Gateway targeting RTF worker namespaces

The Gateway is deployed in a dedicated gateway namespace. The key RTF-specific configuration is allowedRoutes.namespaces.from: Selector with a label selector that targets all RTF worker namespaces at once, regardless of environment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: rtf-gateway
  namespace: gateway
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            rtf.mulesoft.com/role: workers
  - name: https
    port: 443
    protocol: HTTPS
    tls:
      mode: Terminate
      certificateRefs:
      - name: rtf-tls-secret
        kind: Secret
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            rtf.mulesoft.com/role: workers
1
2
3
kubectl create namespace gateway
kubectl apply -f rtf-gateway.yaml
kubectl -n gateway get gateway rtf-gateway

The PROGRAMMED column shows True once the NGINX proxy configuration is live.

Using rtf.mulesoft.com/role: workers as the selector means this single Gateway automatically covers every Anypoint environment deployed on the cluster. New environments added later require no Gateway changes.

Step 5: Grant cross-namespace access with ReferenceGrant

Because the Gateway lives in the gateway namespace and the Mule application Services live in RTF worker namespaces, Kubernetes requires an explicit ReferenceGrant in each worker namespace.

Create one grant per Anypoint environment namespace:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-gateway-to-mule-apps
  namespace: <rtf-worker-namespace>
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: <rtf-worker-namespace>
  to:
  - group: ""
    kind: Service

Replace <rtf-worker-namespace> with the actual namespace (for example the Anypoint environment ID such as env-abc123). Ask the MCP to list your applications to discover the correct namespace:

List all applications in the "Production" environment.

The response includes the Kubernetes namespace and service name for each deployed Mule application.

Step 6: Deploy a Mule application with the Anypoint MCP

If the application is not yet deployed, use the MCP to deploy it and retrieve its service details in one step:

Deploy my application from ./order-api to the "Production" environment
targeting the "prod-rtf" fabric. Use Mule runtime 4.9.4:2e-java17.

Once deployed, confirm the Kubernetes service name:

List applications in "Production" including the application "order-api".

The MCP returns the application name, environment ID (the worker namespace), and deployment status. The Kubernetes Service name matches the application name as registered in Anypoint.

Verify the service exists directly:

1
kubectl get svc -n <rtf-worker-namespace>

Step 7: Create an HTTPRoute per Mule application

Place each HTTPRoute in the same namespace as the Mule application’s service (the RTF worker namespace):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: order-api-route
  namespace: <rtf-worker-namespace>
spec:
  parentRefs:
  - name: rtf-gateway
    namespace: gateway
    sectionName: https
  hostnames:
  - order-api.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: order-api
      port: 8081
1
2
kubectl apply -f order-api-route.yaml
kubectl -n <rtf-worker-namespace> describe httproute order-api-route

Both Accepted and ResolvedRefs conditions must be True. A False status includes a human-readable reason: the most common causes on RTF are a missing ReferenceGrant or an incorrect service port.

Routing multiple Mule applications on the same hostname

If your APIs share a base hostname and are distinguished by path prefix:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: multi-api-route
  namespace: <rtf-worker-namespace>
spec:
  parentRefs:
  - name: rtf-gateway
    namespace: gateway
    sectionName: https
  hostnames:
  - api.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /orders
    backendRefs:
    - name: order-api
      port: 8081
  - matches:
    - path:
        type: PathPrefix
        value: /customers
    backendRefs:
    - name: customer-api
      port: 8081

Step 8: TLS certificate for the Gateway

Create the TLS secret in the gateway namespace (where the Gateway listener reads it):

1
2
3
4
kubectl create secret tls rtf-tls-secret \
  --cert=path/to/cert.pem \
  --key=path/to/key.pem \
  -n gateway

The Gateway already references rtf-tls-secret in the https listener defined in Step 4. Update DNS to point your hostnames at the Gateway’s LoadBalancer IP, then test:

1
curl https://order-api.example.com/api/health

Step 9: Verify the full routing chain

1
2
3
4
5
6
7
8
9
10
11
# All Gateway API resources across all namespaces
kubectl get gatewayclass,gateway,httproute,referencegrant -A

# Status of a specific route
kubectl -n <rtf-worker-namespace> describe httproute order-api-route

# Nginx Gateway Fabric controller logs
kubectl -n nginx-gateway logs -l app.kubernetes.io/name=nginx-gateway-fabric --tail=50

# Mule application logs (via RTF agent or kubectl)
kubectl -n <rtf-worker-namespace> logs -l app=order-api --tail=50

Expected status conditions on each HTTPRoute:

Condition Expected value
Accepted True
ResolvedRefs True

If ResolvedRefs is False with reason RefNotPermitted, the ReferenceGrant is missing or misconfigured.

Migrating from Nginx Ingress on RTF

Nginx Ingress on RTF Gateway API equivalent
kubernetes.io/ingress.class: nginx GatewayClass named nginx, referenced in Gateway
spec.rules[].host on Ingress spec.hostnames[] on HTTPRoute
spec.rules[].http.paths[] spec.rules[].matches[].path on HTTPRoute
spec.tls[].secretName spec.listeners[].tls.certificateRefs[].name on Gateway
Ingress in app namespace with cluster-wide effect HTTPRoute in app namespace, Gateway in gateway namespace
nginx.ingress.kubernetes.io/proxy-read-timeout HTTPRoute spec.rules[].timeouts.backendRequest
No cross-namespace service reference ReferenceGrant in each RTF worker namespace

For each existing Nginx Ingress resource in an RTF namespace:

  1. Create the equivalent HTTPRoute in the same namespace.
  2. Verify the HTTPRoute status shows Accepted: True.
  3. Test the route with curl.
  4. Delete the Ingress resource.
  5. Repeat per application, one at a time.

What to avoid?

Do not place the Gateway in an RTF worker namespace. RTF worker namespaces are managed by the RTF agent. Resources placed there outside of Anypoint Platform can be overwritten or deleted by the agent during application reconciliation. Keep the Gateway and GatewayClass in a separate, agent-unmanaged namespace.

Do not use allowedRoutes.namespaces.from: All in production. This allows any namespace in the cluster to attach routes to your Gateway, including non-RTF workloads. Scope it to RTF worker namespaces using the rtf.mulesoft.com/role: workers label selector.

Do not target port 8082 or 8083 for external traffic. Mule applications on RTF expose their HTTP listener on port 8081. Ports 8082 (HTTPS) and 8083 (management) are used internally by RTF and should not be exposed through the Gateway.

Do not skip ReferenceGrant before testing HTTPRoute status. Without the grant the controller silently marks the route as RefNotPermitted and drops all traffic. Always apply the ReferenceGrant before creating the HTTPRoute.

Do not delete the Nginx Ingress Controller before all HTTPRoutes are verified. Run both controllers in parallel during migration. Confirm each HTTPRoute is Accepted and traffic flows correctly before removing the old Ingress resources and decommissioning the Nginx Ingress Controller.

Do not hardcode the RTF worker namespace name in shared GitOps manifests. The worker namespace is derived from the Anypoint environment ID, which differs between environments. Use a Kustomize overlay or Helm value to parameterise <rtf-worker-namespace> per environment.