Gateway API guide¶
Coxswain implements the Kubernetes Gateway API standard channel. It supports GatewayClass, Gateway, and HTTPRoute resources.
Supported resources¶
| Resource | API version | Support |
|---|---|---|
GatewayClass |
gateway.networking.k8s.io/v1 |
Full |
Gateway |
gateway.networking.k8s.io/v1 |
HTTP and HTTPS listeners only |
HTTPRoute |
gateway.networking.k8s.io/v1 |
Path, header, method, and query matching; weighted traffic split |
ReferenceGrant |
gateway.networking.k8s.io/v1beta1 |
Cross-namespace backend and certificate access |
BackendTLSPolicy |
gateway.networking.k8s.io/v1 |
Upstream TLS configuration referencing a CA ConfigMap or Secret |
Not supported
TCPRoute, TLSRoute, UDPRoute, and GRPCRoute are not implemented. tls.mode: Passthrough on a listener is rejected. The RequestMirror, ExtensionRef, and CORS filters are skipped with a WARN log line.
GatewayClass¶
A GatewayClass identifies a controller implementation. Coxswain claims the class whose spec.controllerName matches coxswain-labs.dev/gateway-controller.
Example¶
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: coxswain
spec:
controllerName: coxswain-labs.dev/gateway-controller # must match --controller-name
Verifying the controller claimed it¶
kubectl get gatewayclass coxswain
# NAME CONTROLLER ACCEPTED
# coxswain coxswain-labs.dev/gateway-controller True
Advertised features¶
Coxswain writes the full list of supported Gateway API features to status.supportedFeatures on the GatewayClass object:
kubectl get gatewayclass coxswain \
-o jsonpath='{.status.supportedFeatures}' | tr ',' '\n'
Implementation-specific capabilities — such as RegularExpression path, header, and query matching — are not listed in supportedFeatures. The Gateway API spec does not define conformance flags for them; they are supported under Coxswain's own dialect. See Implementation-specific matching.
Gateway¶
A Gateway object defines one or more listeners, each binding a port and protocol to a set of allowed routes. Only HTTP and HTTPS listeners are processed; other protocol values are ignored.
Example¶
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: coxswain
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same # Same, All, or Selector
Supported fields¶
| Field | Support |
|---|---|
spec.gatewayClassName |
Full |
spec.listeners[].name |
Full |
spec.listeners[].port |
Full |
spec.listeners[].protocol |
HTTP, HTTPS only |
spec.listeners[].hostname |
Full (wildcard: any number of labels) |
spec.listeners[].allowedRoutes |
Full |
spec.listeners[].tls |
mode: Terminate only; Passthrough rejected |
TLS¶
Add an HTTPS listener and reference a kubernetes.io/tls Secret in the same namespace. Coxswain only supports tls.mode: Terminate — Passthrough is rejected with a status condition. Coxswain reloads the certificate automatically when the Secret changes. See the TLS guide for cert-manager integration.
spec:
gatewayClassName: coxswain
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate # Passthrough is not supported
certificateRefs:
- kind: Secret
name: my-gateway-tls # must exist in the same namespace
allowedRoutes:
namespaces:
from: Same
The referenced Secret must have type: kubernetes.io/tls with tls.crt and tls.key:
apiVersion: v1
kind: Secret
metadata:
name: my-gateway-tls
namespace: default
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded certificate>
tls.key: <base64-encoded private key>
To reference a Secret in a different namespace, create a ReferenceGrant in the namespace where the Secret lives:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-gateway-tls
namespace: certs-namespace # namespace of the Secret
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: default # namespace of the Gateway
to:
- group: ""
kind: Secret
Listener hostnames¶
The hostname field on a listener filters which requests reach its attached routes. Gateway API wildcard matching allows any number of DNS labels: *.example.com matches both foo.example.com and foo.bar.example.com.
An empty hostname accepts requests for any hostname. For SNI-based TLS termination, the listener hostname is also used to select the correct certificate when multiple HTTPS listeners share the same port.
Note
Gateway API wildcards (both listener and HTTPRoute hostnames) match any number of labels. Classic Ingress is more restrictive: *.example.com on an Ingress matches only a single label (foo.example.com yes, foo.bar.example.com no). See the Ingress guide for the Ingress semantics.
Load balancer address¶
Set --status-address to the external IP or hostname of your load balancer. Coxswain writes it to status.addresses on the Gateway object. Without it, the address is left empty.
kubectl get gateway my-gateway
# NAME CLASS ADDRESS PROGRAMMED
# my-gateway coxswain 203.0.113.10 True
Status conditions¶
| Condition | True when |
|---|---|
Accepted |
The controller has claimed this Gateway |
Programmed |
All listeners are configured and ready |
Per-listener conditions are also written: Accepted, ResolvedRefs, and Programmed. Inspect them when a listener is not serving traffic:
kubectl describe gateway my-gateway
HTTPRoute¶
An HTTPRoute defines routing rules and attaches them to one or more Gateway listeners via parentRefs.
Example¶
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route
namespace: default
spec:
parentRefs:
- name: my-gateway # name of the Gateway in the same namespace
hostnames:
- app.example.com # only matched requests for this hostname
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: / # catch-all rule
backendRefs:
- name: frontend-service
port: 80
Supported fields¶
| Field | Support |
|---|---|
spec.parentRefs |
Full (including sectionName and port for targeting a specific listener) |
spec.hostnames |
Full (including wildcards) |
spec.rules[].matches[].path |
PathPrefix, Exact; RegularExpression is implementation-specific (see below) |
spec.rules[].matches[].headers |
Full |
spec.rules[].matches[].method |
Full |
spec.rules[].matches[].queryParams |
Full |
spec.rules[].filters |
See filter table below |
spec.rules[].backendRefs |
Service backends only |
spec.rules[].backendRefs[].weight |
Full |
spec.rules[].backendRefs[].filters |
RequestHeaderModifier, ResponseHeaderModifier only |
Supported filters¶
| Filter | Support |
|---|---|
RequestHeaderModifier |
Supported (rule-level and per-backendRef) |
ResponseHeaderModifier |
Supported (rule-level and per-backendRef) |
URLRewrite |
Supported (hostname and path rewrite) |
RequestRedirect |
Supported (scheme, hostname, port, path, status code) |
RequestMirror |
Not supported — skipped with a WARN log line |
ExtensionRef |
Not supported — skipped with a WARN log line |
CORS |
Not supported — skipped with a WARN log line |
Attaching to a Gateway¶
parentRefs selects the Gateway (and optionally a specific listener by sectionName or port) the route attaches to:
parentRefs:
- name: my-gateway # attach to the whole Gateway
- name: my-gateway
sectionName: https # attach to the listener named "https" only
- name: my-gateway
port: 443 # attach to the listener on port 443 only
The route must be in the same namespace as the Gateway, or the Gateway must set allowedRoutes.namespaces.from: All (or use a Selector).
Path matching¶
type |
Behaviour |
|---|---|
PathPrefix |
Matches requests whose path starts with the given value |
Exact |
Matches only the exact path |
RegularExpression |
Anchored full-path match. Implementation-specific — see below. |
rules:
- matches:
- path:
type: PathPrefix
value: /api # matches /api, /api/users, /api/v2/...
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: Exact
value: /healthz # matches only /healthz
backendRefs:
- name: health-service
port: 8080
Header matching¶
rules:
- matches:
- headers:
- name: X-Tenant
value: acme # only routes requests with this header value
backendRefs:
- name: acme-service
port: 80
Method matching¶
rules:
- matches:
- method: GET # only routes GET requests
backendRefs:
- name: read-service
port: 80
Implementation-specific matching¶
RegularExpression is supported for path, header, and query-parameter matching. These match types are not covered by the Gateway API conformance suite — the spec marks them as implementation-specific and defines no feature flag for them.
Dialect: Rust regex crate — RE2-like syntax. No backreferences, no lookaround. Patterns are case-sensitive by default.
Path regex — anchored to the full request path (^(?:pattern)$ internally). Does not match the query string.
rules:
- matches:
- path:
type: RegularExpression
value: "/item/[0-9]+" # matches /item/42, not /item/abc or /prefix/item/42
backendRefs:
- name: api-service
port: 8080
Header regex — tested against the full header value, unanchored (matches if the pattern appears anywhere in the value). Use ^ and $ to anchor explicitly.
rules:
- matches:
- headers:
- name: X-Tenant
type: RegularExpression
value: "^(acme|globex)$" # matches exactly "acme" or "globex"
backendRefs:
- name: multi-tenant-service
port: 80
Query param regex — same unanchored semantics as header regex.
rules:
- matches:
- queryParams:
- name: version
type: RegularExpression
value: "v[0-9]+" # matches v1, v2, v12, ...
backendRefs:
- name: versioned-service
port: 80
An HTTPRoute with a syntactically invalid regex pattern is rejected: Coxswain sets Accepted: False with reason UnsupportedValue on the affected parentRef.
Wildcard hostnames¶
*.example.com in spec.hostnames matches any number of leading DNS labels: both foo.example.com and foo.bar.example.com match. This is the same semantics applied to listener hostname fields — Gateway API treats wildcards uniformly across listeners and routes.
hostnames:
- "*.example.com" # matches foo.example.com and foo.bar.example.com
Note
Classic Ingress wildcards are more restrictive (single-label only). See the Ingress guide if you also use Ingress objects in the cluster.
Traffic splitting¶
Distribute traffic across multiple backends using weight. Weights are relative and do not need to sum to 100:
rules:
- backendRefs:
- name: service-v1
port: 80
weight: 90 # 90% of traffic
- name: service-v2
port: 80
weight: 10 # 10% of traffic
Cross-namespace backends¶
By default, an HTTPRoute can only reference backends in its own namespace. To allow access to a Service in another namespace, create a ReferenceGrant in the namespace where the Service lives:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-httproute-from-default
namespace: target-namespace # namespace of the Service
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: default # namespace of the HTTPRoute
to:
- group: ""
kind: Service
Routes that reference a backend without a matching ReferenceGrant are rejected with a ResolvedRefs: False condition.
Status conditions¶
| Condition | True when |
|---|---|
Accepted |
The route is attached to a Gateway listener |
Programmed |
The route is active in the data plane |
ResolvedRefs |
All backendRefs resolve to a reachable Service |
Inspect conditions when traffic is not flowing:
kubectl describe httproute my-route