Kgateway policies
Learn how policy inheritance and overrides work for kgateway policies in a route delegation setup.
About policy inheritance
The following policy inheritance and override rules apply for TrafficPolicies:
- Policies that are defined in a TrafficPolicy resource and applied to a parent HTTPRoute resource are automatically inherited by all child and grandchild HTTPRoutes along the route delegation chain.
- If the TrafficPolicy applies to a child or grandchild HTTPRoute and defines a top-level policy that is different from the policy that is defined on the parent, the policies are merged and both the parent and child/grandchild policies are applied. For example, if the parent applies a rate limiting policy and the child applies a transformation policy, both policies are applied.
- If the TrafficPolicy applies to a child or grandchild HTTPRoute and defines the same top-level policy, the policy of the child takes precedence and the policy on the parent is ignored. Note that you can add the
kgateway.dev/inherited-policy-priority: ShallowMergePreferParent
annotation to the parent HTTPRoute to preserve the top-level parent policies on delegated routes. - If you used multiple attachment options to apply a TrafficPolicy with the same top-level policy to a parent or child HTTPRoute, only the policy with the highest priority is applied. The priority is determined by the attachment option that you chose. The following options are sorted from highest to lowest.
- Policy defined in a TrafficPolicy and attached to an HTTPRoute via the HTTPRoute’s
extensionRef
filter - Policy defined in a TrafficPolicy resource and attached to an HTTPRouteRule via the
targetRef.sectionName
option - Policy defined in a TrafficPolicy resource and attached to an HTTPRoute via the
targetRef
option
- Policy defined in a TrafficPolicy and attached to an HTTPRoute via the HTTPRoute’s
Configuration overview
In this guide you walk through a route delegation example where a child HTTPRoute inherits or overrides policies that are set on a parent HTTPRoute.
The following image illustrates the route delegation hierarchy and policy inheritance:
Parent HTTPRoutes:
HTTPRoute | Delegation | Policy merge strategy |
---|---|---|
parent1 |
Delegates to the child-team1 HTTPRoute in team1 namespace on the /anything/team1 prefix path for the delegation-parent1.example domain. |
ShallowMergePreferParent , so that policies of the parent HTTPRoute override the policies of the child HTTPRoute. |
parent2 |
Delegates to the child-team1 HTTPRoute in team1 namespace on the /anything/team1 prefix path for the delegation-parent2.example domain. |
ShallowMergePreferChild , (the default behavior) so that policies of the child HTTPRoute override the policies of the parent HTTPRoute. |
Child HTTPRoute:
HTTPRoute | Delegation | Policy merge strategy |
---|---|---|
child-team1 |
Matches incoming traffic for the /anything/team1/foo prefix path and routes that traffic to the httpbin app in the team1 namespace. |
Policies are merged depending on the parent HTTPRoute’s policy merge strategy for the delegated route. |
TrafficPolicies:
TrafficPolicy | TargetRefs | Description |
---|---|---|
Transformation Policy 1 | parent1 and parent2 HTTPRoutes |
Policy 1 is inherited for the delegation-parent1.example domain, but overridden by the child policy for the delegation-parent2.example domains. |
Local rate limit Policy 2 | parent1 and parent2 HTTPRoutes |
Policy 2 is inherited by both delegated routes because the child does not override the parent policy. |
Transformation Policy 3 | child-team1 HTTPRoute |
Policy 3 does not apply to the delegation-parent1.example domain because that route’s annotation overrides the default child inheritance with the ShallowMergePreferParent annotation. However, Policy 3 applies to the delegation-parent2.example domain because the child policy merge strategy is the default ShallowMergePreferChild . |
Before you begin
-
Follow the Get started guide to install kgateway.
-
Create a Gateway resource and configure an HTTP listener. The following Gateway can serve HTTPRoute resources from all namespaces.
kubectl apply -f- <<EOF kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: http namespace: kgateway-system spec: gatewayClassName: kgateway listeners: - protocol: HTTP port: 8080 name: http allowedRoutes: namespaces: from: All EOF
-
Get the external address of the gateway and save it in an environment variable.
export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS
kubectl port-forward deployment/http -n kgateway-system 8080:8080
-
Create the namespaces for
team1
andteam2
.kubectl create namespace team1 kubectl create namespace team2
-
Deploy the httpbin app into both namespaces.
kubectl -n team1 apply -f https://raw.githubusercontent.com/kgateway-dev/kgateway.dev/main/assets/docs/examples/httpbin.yaml kubectl -n team2 apply -f https://raw.githubusercontent.com/kgateway-dev/kgateway.dev/main/assets/docs/examples/httpbin.yaml
-
Verify that the httpbin apps are up and running.
kubectl get pods -n team1 kubectl get pods -n team2
Example output:
NAME READY STATUS RESTARTS AGE httpbin-f46cc8b9b-bzl9z 3/3 Running 0 7s NAME READY STATUS RESTARTS AGE httpbin-f46cc8b9b-nhtmg 3/3 Running 0 6s
Setup
-
Create the
parent1
HTTPRoute resource that matches incoming traffic on thedelegation-parent1.example
domain. The HTTPRoute resource specifies the following route:/anything/team1
: The routing decision is delegated to a child HTTPRoute resource in theteam1
namespace.kgateway.dev/inherited-policy-priority: ShallowMergePreferParent
: Overrides the default policy inheritance behavior so that parent policies take precedence over child policies.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: parent1 namespace: kgateway-system annotations: kgateway.dev/inherited-policy-priority: ShallowMergePreferParent spec: parentRefs: - name: http namespace: kgateway-system hostnames: - "delegation-parent1.example" rules: - matches: - path: type: PathPrefix value: /anything/team1 backendRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: "*" namespace: team1 EOF
-
Create the
parent2
HTTPRoute resource that matches incoming traffic on thedelegation-parent2.example
domain.anything/team1
: The routing decision is delegated to the same child HTTPRoute resource in theteam1
namespace.kgateway.dev/inherited-policy-priority: ShallowMergePreferChild
: Keeps the default behavior, so that policies of the child HTTPRoute override the policies of the parent HTTPRoute.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: parent2 namespace: kgateway-system annotations: delegation.kgateway.dev/inherited-policy-priority: PreferChild spec: parentRefs: - name: http namespace: kgateway-system hostnames: - "delegation-parent2.example" rules: - matches: - path: type: PathPrefix value: /anything/team1 backendRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: "*" namespace: team1 EOF
-
Create a TrafficPolicy that defines the following policies:
- Transformation policy: The
x-parent-policy: This policy is inherited from the parent.
header is set on any request. - Local rate limiting: Requests to the routes are limited to 1 request per minute.
The TrafficPolicy is applied to the
parent1
andparent2
HTTPRoutes by using thetargetRefs
option.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: parent-policy namespace: kgateway-system spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: parent1 - group: gateway.networking.k8s.io kind: HTTPRoute name: parent2 transformation: request: set: - name: x-parent-policy value: This policy is inherited from the parent. rateLimit: local: tokenBucket: maxTokens: 1 tokensPerFill: 1 fillInterval: 60s EOF
- Transformation policy: The
-
Create the child HTTPRoute resource for the
team1
namespace that matches traffic on the/anything/team1/foo
prefix and routes traffic to the httpbin app in theteam1
namespace.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: child-team1 namespace: team1 spec: rules: - matches: - path: type: PathPrefix value: /anything/team1/foo backendRefs: - name: httpbin port: 8000 EOF
-
Create a TrafficPolicy that defines a custom transformation policy for the
child-team1
HTTPRoute. The policy sets thex-child-team1: This is the child-team1 policy.
request header.kubectl apply -f- <<EOF apiVersion: gateway.kgateway.dev/v1alpha1 kind: TrafficPolicy metadata: name: child-policy namespace: team1 spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: child-team1 transformation: request: set: - name: x-child-team1 value: This is the child-team1 policy. EOF
-
Send a request to the httpbin app on the
delegation.parent1.example
domain. Because theparent1
HTTPRoute annotation lets parent policies override the child policies, the child HTTPRoute inherits the policies that are set on theparent1
HTTPRoute. The TrafficPolicy that you created earlier and applied to thechild-team1
HTTPRoute is ignored. Verify that you see theX-Parent-Policy
header in your response.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/team1/foo \ -H "host: delegation-parent1.example:8080"
curl -i localhost:8080/anything/team1/foo \ -H "host: delegation-parent1.example"
Example output:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 ... { "args": {}, "headers": { "Accept": [ "*/*" ], "Host": [ "delegation-parent1.example:8080" ], "User-Agent": [ "curl/8.7.1" ], ... "X-Parent-Policy": [ "This policy is inherited from the parent." ], "X-Request-Id": [ "db2affa4-2d03-4ee7-8624-75c3f9b40889" ] }, "origin": "10.X.XX.XXX", "url": "http://delegation-parent1.example:8080/anything/team1/foo", "data": "", "files": null, "form": null, "json": null }
-
Send another request to the
delegation.parent1.example
domain. Verify that this time the request is rate limited and a 429 HTTP response is returned, because only 1 request is allowed in a 60 second timeframe.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/team1/foo \ -H "host: delegation-parent1.example:8080"
curl -i localhost:8080/anything/team1/foo \ -H "host: delegation-parent1.example"
Example output:
HTTP/1.1 429 Too Many Requests content-length: 18 content-type: text/plain date: Fri, 06 Jun 2025 15:43:43 GMT server: envoy local_rate_limited%
-
Send a request to the
delegation.parent2.example
domain. Because theparent2
HTTPRoute annotation keeps the default policy merging behavior, the child HTTPRoute overrides any top-level policies that are defined onparent2
. Policies that are not overridden are still inherited from the parent and applied to the child HTTPRoute.Verify that you see the custom
X-Child-Team1
header in your response.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/team1/foo \ -H "host: delegation-parent2.example:8080"
curl -i localhost:8080/anything/team1/foo \ -H "host: delegation-parent2.example"
Example output:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 ... { "args": {}, "headers": { "Accept": [ "*/*" ], "Host": [ "delegation-parent2.example:8080" ], "User-Agent": [ "curl/8.7.1" ], "X-Child-Team1": [ "This is the child-team1 policy." ], "X-Envoy-Expected-Rq-Timeout-Ms": [ "15000" ], ... "origin": "10.X.X.XXX", "url": "http://delegation-parent2.example:8080/anything/team1/foo", "data": "", "files": null, "form": null, "json": null }
-
Send another request to the
delegation.parent2.example
domain. Because the child HTTPRoute does not define any rate limiting policies, it inherits the rate limiting policy ofparent2
. Verify that the request is rate limited and a 429 HTTP response is returned, because only 1 request is allowed in a 60 second timeframe.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/team1/foo \ -H "host: delegation-parent2.example:8080"
curl -i localhost:8080/anything/team1/foo \ -H "host: delegation-parent2.example"
Example output:
HTTP/1.1 429 Too Many Requests content-length: 18 content-type: text/plain date: Fri, 06 Jun 2025 15:43:43 GMT server: envoy local_rate_limited%
Cleanup
You can remove the resources that you created in this guide.kubectl delete gateway http -n kgateway-system
kubectl delete httproute parent1 -n kgateway-system
kubectl delete httproute parent2 -n kgateway-system
kubectl delete httproute child-team1 -n team1
kubectl delete trafficpolicy parent-policy -n kgateway-system
kubectl delete trafficpolicy child-policy -n team1
kubectl delete -n team1 -f https://raw.githubusercontent.com/kgateway-dev/kgateway.dev/main/assets/docs/examples/httpbin.yaml
kubectl delete -n team2 -f https://raw.githubusercontent.com/kgateway-dev/kgateway.dev/main/assets/docs/examples/httpbin.yaml
kubectl delete namespaces team1 team2