Skip to content
Gateway API with kgateway

Gateway API with kgateway in Thalassa Cloud Kubernetes

kgateway is a CNCF project that implements the Kubernetes Gateway API using Envoy as the data plane. It provides a modern, role-oriented alternative to traditional Ingress controllers, with clearer separation between infrastructure operators (who manage Gateways) and application developers (who attach HTTPRoutes).

On Thalassa Cloud Kubernetes, kgateway provisions a LoadBalancer Service for each Gateway, which integrates with Thalassa Cloud VPC Load Balancers. This guide walks you through installing kgateway, configuring a Gateway with Thalassa-specific load balancer settings, and routing traffic to a sample application.

Prerequisites

Before you begin, ensure you have:

  • A running Kubernetes cluster in Thalassa Cloud (Kubernetes 1.24 or later)
  • kubectl configured to access your cluster (tcloud kubernetes connect or a kubeconfig)
  • helm 3.x installed
  • Cluster administrator permissions to install CRDs and cluster-scoped resources

Overview

Exposing an application with kgateway involves:

  1. Installing the Gateway API CRDs and kgateway control plane
  2. Configuring a Gateway (optionally with Thalassa Cloud load balancer annotations)
  3. Deploying your application
  4. Creating an HTTPRoute to connect the Gateway to your Service
    flowchart LR
    Client[Client] -->|HTTPS| LB[Thalassa VPC Load Balancer]
    LB --> GW[kgateway Envoy proxy]
    GW -->|HTTPRoute| SVC[Application Service]
    SVC --> POD[Application Pods]
  

Step 1: Install Gateway API CRDs

Install standard Gateway API CRDs

Install the standard Gateway API CRDs. These provide the core Gateway, HTTPRoute, and GatewayClass resources:

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

Verify the CRDs are installed:

kubectl get crd gateways.gateway.networking.k8s.io
kubectl get crd httproutes.gateway.networking.k8s.io

Step 2: Install kgateway

Install kgateway CRDs

Deploy the kgateway-specific CRDs using Helm:

helm upgrade -i kgateway-crds oci://cr.kgateway.dev/kgateway-dev/charts/kgateway-crds \
  --create-namespace \
  --namespace kgateway-system \
  --version v2.3.1

Install the kgateway control plane

Install the kgateway controller:

helm upgrade -i kgateway oci://cr.kgateway.dev/kgateway-dev/charts/kgateway \
  --namespace kgateway-system \
  --version v2.3.1

Verify the installation

Confirm the control plane is running and the default GatewayClass is available:

kubectl get pods -n kgateway-system
kubectl get gatewayclass kgateway

You should see the kgateway pod in Running state and the kgateway GatewayClass with Accepted=True.

Step 3: Configure a Gateway

When you create a Gateway resource, kgateway automatically provisions an Envoy proxy Deployment and a LoadBalancer Service. On Thalassa Cloud, this Service is backed by a VPC Load Balancer.

Basic Gateway

Create a Gateway with an HTTP listener on port 80:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: http
  namespace: kgateway-system
spec:
  gatewayClassName: kgateway
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All

Apply the Gateway:

kubectl apply -f gateway.yaml

Verify the Gateway is programmed and has an external address:

kubectl get gateway http -n kgateway-system
kubectl get svc -n kgateway-system

It may take a minute or two for the Thalassa Cloud Load Balancer to receive an external IP address.

Gateway with Thalassa Cloud Load Balancer settings

Use a GatewayParameters resource to apply Thalassa-specific annotations to the generated LoadBalancer Service. This lets you configure ACLs, security groups, internal load balancers, and other VPC load balancer features.

Create gateway-parameters.yaml:

apiVersion: gateway.kgateway.dev/v1alpha1
kind: GatewayParameters
metadata:
  name: thalassa-lb
  namespace: kgateway-system
spec:
  kube:
    serviceOverlay:
      metadata:
        annotations:
          loadbalancer.k8s.thalassa.cloud/create-security-group: "true"
          loadbalancer.k8s.thalassa.cloud/acl-allowed-sources: "0.0.0.0/0"

Then reference it in your Gateway:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: http
  namespace: kgateway-system
spec:
  gatewayClassName: kgateway
  infrastructure:
    parametersRef:
      group: gateway.kgateway.dev
      kind: GatewayParameters
      name: thalassa-lb
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All

Load Balancer annotations

All Thalassa Cloud load balancer annotations can be applied via GatewayParameters serviceOverlay. Common options include loadbalancer.k8s.thalassa.cloud/internal for internal-only load balancers, loadbalancer.k8s.thalassa.cloud/enable-proxy-protocol for passing client IP, and per-port ACLs with loadbalancer.k8s.thalassa.cloud/acl-port-{port}.

Step 4: Deploy a sample application

Deploy the httpbin sample app to test routing:

kubectl apply -f https://raw.githubusercontent.com/kgateway-dev/kgateway/refs/heads/main/examples/httpbin.yaml

Verify the app is running:

kubectl get pods -n httpbin

Step 5: Create an HTTPRoute

An HTTPRoute attaches your application to a Gateway listener. Create the route in the same namespace as the backend Service:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
spec:
  parentRefs:
  - name: http
    namespace: kgateway-system
    sectionName: http
  hostnames:
  - "www.example.com"
  rules:
  - backendRefs:
    - name: httpbin
      port: 8000

Apply the HTTPRoute:

kubectl apply -f httproute.yaml

Verify the route is accepted:

kubectl get httproute httpbin -n httpbin -o yaml

Look for Accepted=True and ResolvedRefs=True in the status conditions.

Step 6: Verify traffic routing

Get the external address of the Gateway load balancer:

export GATEWAY_ADDRESS=$(kubectl get svc -n kgateway-system http \
  -o=jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
echo $GATEWAY_ADDRESS

Point your DNS record for www.example.com to this address, then send a test request:

curl -i http://$GATEWAY_ADDRESS/headers -H "host: www.example.com"

You should receive a 200 OK response from httpbin with the request headers echoed back.

TLS with Cert Manager

For HTTPS, install Cert Manager and configure a ClusterIssuer. Cert Manager supports the Gateway API through the gatewayHTTPRoute solver.

Create a ClusterIssuer with Gateway API support:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    - http01:
        gatewayHTTPRoute:
          parentRefs:
          - name: http
            namespace: kgateway-system
            kind: Gateway

Then reference the TLS secret in your Gateway listener (as shown in Step 3) or annotate the HTTPRoute:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httpbin
  namespace: httpbin
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  parentRefs:
  - name: http
    namespace: kgateway-system
    sectionName: https
  hostnames:
  - "www.example.com"
  rules:
  - backendRefs:
    - name: httpbin
      port: 8000

Cert Manager automatically provisions and renews the certificate, storing it in the referenced Secret.

Troubleshooting

Gateway not getting an external address

Check the LoadBalancer Service status and events:

kubectl describe svc -n kgateway-system http
kubectl get events -n kgateway-system --sort-by='.lastTimestamp'

Ensure your cluster has a working Cloud Controller Manager and that the subnet has available IP addresses for load balancers.

HTTPRoute not accepted

Inspect the HTTPRoute status and kgateway controller logs:

kubectl describe httproute <route-name> -n <namespace>
kubectl logs -n kgateway-system -l app.kubernetes.io/name=kgateway

Common causes:

  • Parent Gateway not ready: Wait for the Gateway to show Programmed=True
  • Namespace mismatch: HTTPRoutes must be in the same namespace as the backend Service, or use a ReferenceGrant
  • Listener mismatch: Ensure sectionName in parentRefs matches a listener name on the Gateway

Gateway proxy pod in CrashLoopBackOff

Check the proxy pod logs:

kubectl logs -n kgateway-system -l gateway.networking.k8s.io/gateway-name=http

Verify that Gateway API CRDs and kgateway CRDs are both installed and that no conflicting ingress controllers are binding the same ports.

Cleanup

Remove the resources created in this guide:

kubectl delete httproute httpbin -n httpbin
kubectl delete gateway http -n kgateway-system
kubectl delete gatewayparameters thalassa-lb -n kgateway-system
kubectl delete -f https://raw.githubusercontent.com/kgateway-dev/kgateway/refs/heads/main/examples/httpbin.yaml
helm uninstall kgateway -n kgateway-system
helm uninstall kgateway-crds -n kgateway-system

Next steps