Expose workloads with Gateway API and kgateway on Thalassa Cloud Kubernetes

Install kgateway, provision a VPC Load Balancer per Gateway, and route traffic with HTTPRoutes - the modern alternative to Ingress on Thalassa Cloud.

2026-06-22
Thalassa Cloud
4 min read

Ingress controllers have served Kubernetes well for years, but they bundle concerns that teams often want to separate: platform operators manage the edge, while application developers attach routes to their services. The Kubernetes Gateway API was designed for that approach, and kgateway implements it with Envoy as the data plane.

On Thalassa Cloud Kubernetes, each Gateway you create provisions an Envoy proxy and a LoadBalancer Service backed by a VPC Load Balancer. That means kgateway fits naturally into our networking model: ACLs, security groups, internal load balancers, and proxy protocol are all available through the same annotations you would use on any Kubernetes Service of type LoadBalancer.

This post walks through installing kgateway, exposing a sample app, and applying Thalassa-specific load balancer settings. For the full command reference and troubleshooting section, see the Gateway API with kgateway guide in our documentation.

Why Gateway API and kgateway

Traditional Ingress ties hostname routing, TLS termination, and backend selection to a single resource with implementation-specific annotations. Gateway API introduces distinct roles:

  • Platform teams own Gateway resources - listeners, TLS, and the external entry point
  • Application teams attach HTTPRoute resources that reference a Gateway and point at their Services

kgateway is a CNCF project that watches those resources and programs Envoy accordingly. On Thalassa Cloud, the traffic path looks like this:

Client → Thalassa VPC Load Balancer → kgateway Envoy proxy → HTTPRoute → Service → Pods

Prerequisites

Before you start, you need:

  • A running Thalassa Cloud Kubernetes cluster (1.24 or later)
  • kubectl configured (tcloud kubernetes connect or a kubeconfig)
  • Helm 3.x
  • Cluster admin permissions to install CRDs

Install Gateway API and kgateway

Install the standard Gateway API CRDs:

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

Then install kgateway CRDs and the control plane:

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

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

Verify the installation:

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

You should see the controller pod running and the kgateway GatewayClass with Accepted=True.

Configure a Gateway

Creating a Gateway tells kgateway to deploy an Envoy proxy and a LoadBalancer Service. A minimal 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 it and wait for the Thalassa Cloud Load Balancer to receive an external address - typically a minute or two:

kubectl apply -f gateway.yaml
kubectl get gateway http -n kgateway-system
kubectl get svc -n kgateway-system

Thalassa Cloud load balancer settings

For production gateways you usually want more than a default public load balancer. Use a GatewayParameters resource to inject Thalassa annotations onto the generated Service:

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"

Reference it from your Gateway via infrastructure.parametersRef:

spec:
  gatewayClassName: kgateway
  infrastructure:
    parametersRef:
      group: gateway.kgateway.dev
      kind: GatewayParameters
      name: thalassa-lb
  listeners:
  # ... same as above

All Thalassa load balancer annotations work through serviceOverlay. Common options:

  • loadbalancer.k8s.thalassa.cloud/internal - internal-only load balancer
  • loadbalancer.k8s.thalassa.cloud/enable-proxy-protocol - pass client IP to Envoy
  • loadbalancer.k8s.thalassa.cloud/acl-port-{port} - per-port ACL rules

See Service Load Balancers for the full annotation reference.

Route traffic with an HTTPRoute

Deploy a sample backend - httpbin works well for testing:

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

Attach it to the Gateway with an HTTPRoute 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

Check that the route is accepted (Accepted=True, ResolvedRefs=True):

kubectl get httproute httpbin -n httpbin -o yaml

Get the Gateway address and send a test request:

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

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

Point your DNS record for www.example.com at $GATEWAY_ADDRESS for real hostname-based routing.

Add TLS with Cert Manager

For HTTPS, install Cert Manager and configure a ClusterIssuer that uses the Gateway API HTTP-01 solver:

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

Annotate your HTTPRoute with cert-manager.io/cluster-issuer: letsencrypt-prod and attach it to an HTTPS listener on the Gateway. Cert Manager provisions and renews the certificate automatically.

Troubleshooting quick reference

SymptomWhat to check
No external address on the Gateway Servicekubectl describe svc -n kgateway-system http - confirm Cloud Controller Manager is healthy and the subnet has LB IP capacity
HTTPRoute not acceptedGateway must show Programmed=True; sectionName must match a listener; backend Service must be in the same namespace (or use a ReferenceGrant)
Envoy proxy in CrashLoopBackOffProxy logs: kubectl logs -n kgateway-system -l gateway.networking.k8s.io/gateway-name=http - verify both Gateway API and kgateway CRDs are installed

For detailed diagnostics and cleanup commands, see the full guide.


Related posts