
Install kgateway, provision a VPC Load Balancer per Gateway, and route traffic with HTTPRoutes - the modern alternative to Ingress on Thalassa Cloud.
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.
Traditional Ingress ties hostname routing, TLS termination, and backend selection to a single resource with implementation-specific annotations. Gateway API introduces distinct roles:
Gateway resources - listeners, TLS, and the external entry pointHTTPRoute resources that reference a Gateway and point at their Serviceskgateway 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
Before you start, you need:
kubectl configured (tcloud kubernetes connect or a kubeconfig)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.
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
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 balancerloadbalancer.k8s.thalassa.cloud/enable-proxy-protocol - pass client IP to Envoyloadbalancer.k8s.thalassa.cloud/acl-port-{port} - per-port ACL rulesSee Service Load Balancers for the full annotation reference.
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.
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.
| Symptom | What to check |
|---|---|
| No external address on the Gateway Service | kubectl describe svc -n kgateway-system http - confirm Cloud Controller Manager is healthy and the subnet has LB IP capacity |
| HTTPRoute not accepted | Gateway must show Programmed=True; sectionName must match a listener; backend Service must be in the same namespace (or use a ReferenceGrant) |
| Envoy proxy in CrashLoopBackOff | Proxy 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.