Set up a built-in Kubernetes gateway with Kong Mesh

Uses: Kong Mesh
TL;DR

Install the Gateway API CRDs, create a GatewayClass and a Gateway to configure the built-in gateway, then create an HTTPRoute and allow traffic to the gateway with a MeshTrafficPermission. To secure your endpoint, generate a certificate and add it to the Gateway.

Prerequisites

You will need Helm, a package manager for Kubernetes.

This guide requires a running Kubernetes cluster that supports the LoadBalancer service type. If you already have a Kubernetes cluster running, you can skip this step. It can be a cluster running locally, like Docker, or in a public cloud like AWS EKS, GCP GKE, etc.

  1. Install Kong Mesh:

    helm repo add kong-mesh https://kong.github.io/kong-mesh-charts
    helm repo update
    helm upgrade \
      --install \
      --create-namespace \
      --namespace kong-mesh-system \
      kong-mesh kong-mesh/kong-mesh
    kubectl wait -n kong-mesh-system --for=condition=ready pod --selector=app=kong-mesh-control-plane --timeout=90s
    
  2. Apply the demo configuration:

    echo "
    apiVersion: v1
    kind: Namespace
    metadata:
      labels:
        kuma.io/sidecar-injection: enabled
      name: kong-mesh-demo
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-app
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: demo-app
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-app-v1
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: demo-app
        version: v1
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-app-v2
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: demo-app
        version: v2
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: kv
      namespace: kong-mesh-demo
    spec:
      ports:
      - appProtocol: http
        port: 5050
        protocol: TCP
        targetPort: 5050
      selector:
        app: kv
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: demo-app
        version: v1
      name: demo-app
      namespace: kong-mesh-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: demo-app
          version: v1
      template:
        metadata:
          labels:
            app: demo-app
            version: v1
        spec:
          containers:
          - env:
            - name: OTEL_SERVICE_NAME
              value: demo-app
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://opentelemetry-collector.mesh-observability:4317
            - name: KV_URL
              value: http://kv.kong-mesh-demo.svc.cluster.local:5050
            - name: APP_VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
            image: ghcr.io/kumahq/kuma-counter-demo:latest@sha256:daf8f5cffa10b576ff845be84e4e3bd5a8a6470c7e66293c5e03a148f08ac148
            name: app
            ports:
            - containerPort: 5050
              name: http
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: demo-app
        version: v2
      name: demo-app-v2
      namespace: kong-mesh-demo
    spec:
      replicas: 0
      selector:
        matchLabels:
          app: demo-app
          version: v2
      template:
        metadata:
          labels:
            app: demo-app
            version: v2
        spec:
          containers:
          - env:
            - name: OTEL_SERVICE_NAME
              value: demo-app
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://opentelemetry-collector.mesh-observability:4317
            - name: KV_URL
              value: http://kv.kong-mesh-demo.svc.cluster.local:5050
            - name: APP_VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
            image: ghcr.io/kumahq/kuma-counter-demo:latest@sha256:daf8f5cffa10b576ff845be84e4e3bd5a8a6470c7e66293c5e03a148f08ac148
            name: demo-app
            ports:
            - containerPort: 5050
              name: http
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: kv
      namespace: kong-mesh-demo
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: kv
      template:
        metadata:
          labels:
            app: kv
        spec:
          containers:
          - env:
            - name: OTEL_SERVICE_NAME
              value: kv
            - name: OTEL_EXPORTER_OTLP_ENDPOINT
              value: http://opentelemetry-collector.mesh-observability:4317
            - name: APP_VERSION
              valueFrom:
                fieldRef:
                  fieldPath: metadata.labels['version']
            image: ghcr.io/kumahq/kuma-counter-demo:latest@sha256:daf8f5cffa10b576ff845be84e4e3bd5a8a6470c7e66293c5e03a148f08ac148
            name: app
            ports:
            - containerPort: 5050
              name: http
    ---
    apiVersion: kuma.io/v1alpha1
    kind: Mesh
    metadata:
      name: default
    spec:
      meshServices:
        mode: Exclusive
      mtls:
        backends:
        - name: ca-1
          type: builtin
        enabledBackend: ca-1
    ---
    apiVersion: kuma.io/v1alpha1
    kind: MeshTrafficPermission
    metadata:
      name: kv
      namespace: kong-mesh-demo
    spec:
      from:
      - default:
          action: Allow
        targetRef:
          kind: MeshSubset
          tags:
            app: demo-app
            k8s.kuma.io/namespace: kong-mesh-demo
      targetRef:
        kind: Dataplane
        labels:
          app: kv" | kubectl apply -f -
    

To get traffic from outside your mesh with Kong Mesh, you can use a built-in gateway.

With the demo configuration, traffic can only get in the mesh by port-forwarding to an instance of an app inside the mesh. In production, you typically set up a gateway to receive traffic external to the mesh. In this guide we’ll use the Kubernetes Gateway API to add a built-in gateway in front of the demo-app service and expose it publicly.

Install the Gateway API CRDs

  1. Run the following command to install the Kubernetes Gateway API CRDs:
  kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
  1. Create a GatewayClass resource:
    echo "apiVersion: gateway.networking.k8s.io/v1
    kind: GatewayClass
    metadata:
      name: built-in-gateway
    spec:
      controllerName: gateways.kuma.io/controller" | kubectl apply -f -
    
  2. Restart the Kong Mesh control plane to apply the changes:
    kubectl rollout restart deployment kong-mesh-control-plane -n kong-mesh-system
    kubectl wait -n kong-mesh-system --for=condition=ready pod --selector=app=kong-mesh-control-plane --timeout=90s
    

Configure the gateway

  1. Create a Gateway resource to configure the pods that will run the gateway:

    echo "apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: built-in-gateway
      namespace: kong-mesh-demo
    spec:
      gatewayClassName: built-in-gateway
      listeners:
       - name: proxy
         port: 8080
         protocol: HTTP" | kubectl apply -f -
    
  2. Validate that the pods are running:

    kubectl wait -n kong-mesh-demo --for=condition=ready pod --selector=app=built-in-gateway --timeout=90s
    kubectl get pods -n kong-mesh-demo
    

    You should see the following result:

    NAME                               READY   STATUS    RESTARTS   AGE
    built-in-gateway-c759dffc8-w7nlt   1/1     Running   0          9s
    demo-app-84d96db569-6t8kx          2/2     Running   0          106s
    kv-648747567c-qhmxj                2/2     Running   0          106s
    
  3. Export the gateway’s public IP:

    export PROXY_IP=$(kubectl get svc -n kong-mesh-demo built-in-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $PROXY_IP
    
  4. Send a request to the gateway to validate that it’s running:

    curl -i $PROXY_IP:8080
    

    Since we haven’t configured any Routes, you should see the following result:

    HTTP/1.1 404 Not Found
    content-length: 62
    content-type: text/plain
    vary: Accept-Encoding
    date: Tue, 06 Jan 2026 14:36:29 GMT
    server: Kuma Gateway
       
    This is a Kuma MeshGateway. No routes match this MeshGateway!
    

Create a Route

  1. Create a Route with the MeshHTTPRoute resource and associate it with the built-in gateway:

    echo "apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: echo
      namespace: kong-mesh-demo
    spec:
      parentRefs:
        - group: gateway.networking.k8s.io
          kind: Gateway
          name: built-in-gateway
          namespace: kong-mesh-demo
      rules:
        - backendRefs:
          - kind: Service
            name: demo-app
            port: 5050
            weight: 1
          matches:
            - path:
                type: PathPrefix
                value: /" | kubectl apply -f -
    
  2. Send a request to the gateway:

    curl -i $PROXY_IP:8080
    

    Now the Route exists, but the gateway can’t access the demo app service because of the permissions applied in the demo configuration:

    HTTP/1.1 403 Forbidden
    content-length: 19
    content-type: text/plain
    date: Tue, 06 Jan 2026 14:37:19 GMT
    server: Kuma Gateway
    x-envoy-upstream-service-time: 0
       
    RBAC: access denied%      
    
  3. Add a MeshTrafficPermission resource to allow traffic to the Service:

    echo "apiVersion: kuma.io/v1alpha1
    kind: MeshTrafficPermission
    metadata:
      namespace: kong-mesh-demo 
      name: demo-app
    spec:
      targetRef:
        kind: Dataplane
        labels:
          app: demo-app
      from:
        - targetRef:
            kind: MeshSubset
            tags: 
              kuma.io/service: built-in-gateway_kong-mesh-demo_svc 
          default:
            action: Allow" | kubectl apply -f -
    
  4. Send a request to the Route:

    curl -XPOST -i $PROXY_IP:8080/api/counter
    

    You should get the following result:

    {"counter":1,"zone":""}
    
Something wrong?

Help us make these docs great!

Kong Developer docs are open source. If you find these useful and want to make them better, contribute today!