The problem
When a GRPCRoute and an HTTPRoute are attached to the same Gateway listener section (sectionName), Cilium's Gateway API translator generates a combined Envoy filter chain that:
- Does not add the
grpc_web and grpc_stats HTTP filters required for gRPC traffic
- Sets the codec to
HTTP1, blocking HTTP/2 negotiation
This causes all external gRPC connections to fail — peers connecting from outside the cluster receive an HTTP/1.1 response instead of a valid gRPC response. In-cluster traffic works because Cilium's eBPF kube-proxy replacement bypasses Envoy's filter chain processing (including the HTTP1 codec restriction) for in-cluster sources — though Cilium still consults Envoy's route tables, so a matching GRPCRoute must still exist on the listener.
The chart provides no documentation about this constraint. Users naturally configure the grpcRoute on the same listener as the httpRoute (same sectionName in parentRefs), which appears to work for in-cluster components but silently breaks external gRPC clients.
The suggested fix
No template changes are required — parentRefs is already freely configurable per-route. The fix is documentation in values.yaml under server.grpcRoute explaining:
- Cilium requires a dedicated Gateway listener section exclusively for the GRPCRoute (no HTTPRoute on the same sectionName)
- The
dashboard.config.mgmtGrpcApiEndpoint should point to a hostname served by that dedicated listener
- An example showing the correct parentRefs setup
The current workaround
Create a dedicated Gateway listener (e.g. netbird-grpc-https) for gRPC only. If in-cluster components such as the Netbird operator or NetworkRouter are used, they connect to the original hostname for both REST (/api/*) and gRPC — that hostname cannot be pointed at the dedicated gRPC listener because it has no HTTPRoute for REST. In that case, attach the GRPCRoute to both listener sections via dual parentRefs. Without in-cluster components, a single parentRef on the dedicated listener is sufficient.
dashboard:
config:
mgmtGrpcApiEndpoint: https://netbird-grpc.example.com
server:
grpcRoute:
hostnames:
- netbird.example.com # original, for in-cluster components
- netbird-grpc.example.com # dedicated gRPC listener, for external clients
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: shared-gateway
namespace: gateway
sectionName: netbird-https # shared with HTTPRoutes (in-cluster)
- group: gateway.networking.k8s.io
kind: Gateway
name: shared-gateway
namespace: gateway
sectionName: netbird-grpc-https # gRPC-only (external clients)
Requirements
For both the workaround and once the documentation fix lands:
- When using Cilium: setting the Helm Value
gatewayAPI.enableAlpn=true makes Cilium advertise h2 in TLS ALPN on the dedicated listener so gRPC clients negotiate HTTP/2
- External clients must use
netbird-grpc.example.com as their Management URL — the original hostname shares a listener with HTTPRoutes and will continue to fail gRPC for external connections even after applying the workaround.
The problem
When a
GRPCRouteand anHTTPRouteare attached to the same Gateway listener section (sectionName), Cilium's Gateway API translator generates a combined Envoy filter chain that:grpc_webandgrpc_statsHTTP filters required for gRPC trafficHTTP1, blocking HTTP/2 negotiationThis causes all external gRPC connections to fail — peers connecting from outside the cluster receive an HTTP/1.1 response instead of a valid gRPC response. In-cluster traffic works because Cilium's eBPF kube-proxy replacement bypasses Envoy's filter chain processing (including the HTTP1 codec restriction) for in-cluster sources — though Cilium still consults Envoy's route tables, so a matching GRPCRoute must still exist on the listener.
The chart provides no documentation about this constraint. Users naturally configure the
grpcRouteon the same listener as thehttpRoute(samesectionNameinparentRefs), which appears to work for in-cluster components but silently breaks external gRPC clients.The suggested fix
No template changes are required — parentRefs is already freely configurable per-route. The fix is documentation in
values.yamlunderserver.grpcRouteexplaining:dashboard.config.mgmtGrpcApiEndpointshould point to a hostname served by that dedicated listenerThe current workaround
Create a dedicated Gateway listener (e.g.
netbird-grpc-https) for gRPC only. If in-cluster components such as the Netbird operator or NetworkRouter are used, they connect to the original hostname for both REST (/api/*) and gRPC — that hostname cannot be pointed at the dedicated gRPC listener because it has noHTTPRoutefor REST. In that case, attach theGRPCRouteto both listener sections via dualparentRefs. Without in-cluster components, a singleparentRefon the dedicated listener is sufficient.Requirements
For both the workaround and once the documentation fix lands:
gatewayAPI.enableAlpn=truemakes Cilium advertiseh2in TLS ALPN on the dedicated listener so gRPC clients negotiate HTTP/2netbird-grpc.example.comas their Management URL — the original hostname shares a listener with HTTPRoutes and will continue to fail gRPC for external connections even after applying the workaround.