Kgateway policies

Learn how policy inheritance and overrides work for kgateway policies in a route delegation setup.

ℹ️
Want to learn more about policy inheritance and overrides for Kubernetes Gateway API-native policies? See Native Gateway API policies.
⚠️
This feature is available in kgateway version 2.1.x or later.

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

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

  1. Follow the Get started guide to install kgateway.

  2. 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
  3. 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

  4. Create the namespaces for team1 and team2.

    kubectl create namespace team1
    kubectl create namespace team2
  5. 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
  6. 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

  1. Create the parent1 HTTPRoute resource that matches incoming traffic on the delegation-parent1.example domain. The HTTPRoute resource specifies the following route:

    • /anything/team1: The routing decision is delegated to a child HTTPRoute resource in the team1 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
  2. Create the parent2 HTTPRoute resource that matches incoming traffic on the delegation-parent2.example domain.

    • anything/team1: The routing decision is delegated to the same child HTTPRoute resource in the team1 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
  3. 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 and parent2 HTTPRoutes by using the targetRefs 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
  4. 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 the team1 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
  5. Create a TrafficPolicy that defines a custom transformation policy for the child-team1 HTTPRoute. The policy sets the x-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
  6. Send a request to the httpbin app on the delegation.parent1.example domain. Because the parent1 HTTPRoute annotation lets parent policies override the child policies, the child HTTPRoute inherits the policies that are set on the parent1 HTTPRoute. The TrafficPolicy that you created earlier and applied to the child-team1 HTTPRoute is ignored. Verify that you see the X-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
    }
    
  7. 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%
    
  8. Send a request to the delegation.parent2.example domain. Because the parent2 HTTPRoute annotation keeps the default policy merging behavior, the child HTTPRoute overrides any top-level policies that are defined on parent2. 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
    }
    
  9. 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 of parent2. 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