Expose the gateway on a generic cluster
On a cluster that is not AKS, EKS, or GKE, no cloud load balancer integration exists and tare install-gateway has no provider to drive. The gateway is exposed instead through whatever already terminates traffic at the edge of the cluster, typically an Ingress controller. The mechanics are ordinary Kubernetes; this page exists for the three details that are not obvious from the chart and that each cost an afternoon in the field.
This page applies to self-managed clusters: k3s, kubeadm, OpenShift, or any distribution where inbound traffic arrives through an Ingress controller, a tunnel, or a bare NodePort rather than a provisioned cloud load balancer. The data plane itself is installed first, through the Helm or GitOps path.
Where the Service actually is
Two facts about the egress topology prevent the most common wrong turn:
- Envoy Gateway creates the data plane's Service in the Gateway's namespace, not the controller's. An Ingress or ExternalName Service pointed at the controller namespace finds nothing to route to. Confirm the namespace before wiring anything:
kubectl get svc -A -l gateway.envoyproxy.io/owning-gateway-namelists the generated Services with the namespace each landed in. - The generated Service name is hashed and changes. Without pinning, Envoy Gateway derives the Service name from the Gateway, and there is nothing stable for an Ingress backend to target across reinstalls. Pin the name through the egress
EnvoyProxyresource before pointing anything at it:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: tars-egress
spec:
provider:
type: Kubernetes
kubernetes:
envoyService:
name: tars-egress-svc
The same EnvoyProxy patch mechanism is what the AWS path uses to request a Network Load Balancer, so a rendered manifest set already contains the resource to edit.
Point the edge at the pinned Service
With the Service name pinned, the wiring is standard:
- Same namespace: an Ingress rule with the pinned Service as its backend, on the gateway's HTTPS port.
- Ingress controller in another namespace: an
ExternalNameService in the controller's namespace resolving totars-egress-svc.<gateway-namespace>.svc.cluster.local, with the Ingress backend pointing at the ExternalName. The Ingress controller must be configured to pass SNI and theHostheader through unchanged, since hostname matching on the gateway's routes is exact. - A tunnel in front (for example an outbound-only edge): terminate the tunnel on the Ingress controller and treat everything below it identically.
The public URL that results is registered on the management plane in the Admin Console under System → Settings → Data planes, the same registration every path uses. The membership probe contract is standard port 443 only; a listener on a non-standard port needs a standard-port endpoint in front before DNS health checks can target it (see Gateway sets and DNS-level failover).
The NetworkPolicy rule that does not look related
On a cluster that enforces NetworkPolicy, the Ingress controller needs an egress rule that names the gateway pods, not the Service in front of them. The policy is evaluated against the destination pod after DNAT, so a rule written for the ExternalName Service, or for the Service's cluster IP, covers nothing: the connection is refused at the pod, and the only visible symptom is a 502 in the Ingress controller's log with connection refused against a cluster IP.
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: <gateway-namespace>
podSelector:
matchLabels:
gateway.envoyproxy.io/owning-gateway-name: tars-egress
A 502 with connection refused from the Ingress controller, while kubectl exec from another pod in an unrestricted namespace reaches the gateway fine, is this rule missing.
Verify
- The root of the gateway endpoint answers with the JSON status document, no API key needed;
"status": "serving"means routes are deployed. See Health and status visibility. GET /v1/modelswith an API key returns the models the key can reach. An empty list at this point is configuration, not exposure: see the note on model grants in the installation guides.- The registered URL serves the membership probe green before DNS is pointed at it.
Where to go next