Weighted routing
By default, the Kubernetes Gateway API sorts HTTPRoute rules based on their order and specificity, as defined in the Gateway API docs. When a route matches a request, no further routes are evaluated for matching, which might affect the gateway’s routing decision and the policies that are applied to the traffic. With kgateway, you can configure weights for more fine-grained control over your routing rules.
Before you begin
-
Follow the Get started guide to install kgateway.
-
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
-
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
Step 1: Review the default routing behavior
By default, the Kubernetes Gateway API sorts HTTPRoute rules as defined in the Gateway API docs.
- Specificity of the route matching rule, such as a header match with largest number of matching headers.
- Oldest route based on creation timestamp.
- Alphabetical order of the route based on the
namespace/name
of the route. - Order of the route in the list of the HTTPRoute resource.
Review route precedence in action:
-
Deploy a hello-world app to demonstrate different backend responses.
kubectl apply -f- <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: hello-world namespace: httpbin spec: replicas: 1 selector: matchLabels: app: hello-world template: metadata: labels: app: hello-world spec: containers: - name: hello-world image: nginxdemos/hello:plain-text ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: hello-world namespace: httpbin spec: selector: app: hello-world ports: - port: 80 targetPort: 80 EOF
-
Update the HTTPRoute to demonstrate the default Gateway API precedence behavior by configuring two routes:
/anything
that routes traffic to the httpbin app and/anything/a
that targets the hello-world app you just deployed.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - "www.example.com" rules: - matches: - path: type: PathPrefix value: /anything backendRefs: - name: httpbin port: 8000 - matches: - path: type: PathPrefix value: /anything/a backendRefs: - name: hello-world port: 80 EOF
-
Send a request to the
/anything/a
path. The request matches both the rules for/anything
and/anything/a
. Because the more specific/anything/a
rule takes precedence, the request routes to the hello-world service instead of the httpbin service.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/a -H "host: www.example.com:8080"
curl -i localhost:8080/anything/a -H "host: www.example.com"
Example output from the hello-world service:
HTTP/1.1 200 OK server: envoy date: Tue, 29 Jul 2025 20:07:36 GMT content-type: text/plain content-length: 167 expires: Tue, 29 Jul 2025 20:07:35 GMT cache-control: no-cache x-envoy-upstream-service-time: 0 Server address: 10.244.0.8:80 Server name: hello-world-669dfbd799-g4jkg Date: 29/Jul/2025:20:07:36 +0000 URI: /anything/a Request ID: 796644ce8bda8ae5ecc36c5f4117a590
-
Send a request to the
/anything
path. This example routes to the httpbin service because it only matches the less specific rule.curl -i http://$INGRESS_GW_ADDRESS:8080/anything -H "host: www.example.com:8080"
curl -i localhost:8080/anything -H "host: www.example.com"
Example output from the httpbin service:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 date: Tue, 29 Jul 2025 20:08:21 GMT content-length: 587 x-envoy-upstream-service-time: 1 server: envoy
{ "args": {}, "headers": { "Accept": [ "*/*" ], "Host": [ "www.example.com" ], "User-Agent": [ "curl/8.7.1" ], "X-Envoy-Expected-Rq-Timeout-Ms": [ "15000" ], "X-Envoy-External-Address": [ "127.0.0.1" ], "X-Forwarded-For": [ "10.244.0.7" ], "X-Forwarded-Proto": [ "http" ], "X-Request-Id": [ "7ec1f5f7-72b7-4053-b2e4-0117a2438d4c" ] }, "origin": "10.244.0.7", "url": "http://www.example.com/anything", "data": "", "files": null, "form": null, "json": null }
Step 2: Enable weighted route precedence
By default, weighted routes are disabled. Upgrade your kgateway Helm installation to enable the feature.
-
Get the Helm values for your current Helm installation.
helm get values kgateway -n kgateway-system -o yaml > kgateway.yaml open kgateway.yaml
-
Add the following values to the Helm values file to enable the weighted routes feature in kgateway.
controller: extraEnv: KGW_WEIGHTED_ROUTE_PRECEDENCE: true
-
Upgrade your Helm installation. Replace the
--version v2.0.3
option to match your current version.helm upgrade -i --namespace kgateway-system --version v2.0.3 kgateway oci://cr.kgateway.dev/kgateway-dev/charts/kgateway -f kgateway.yaml
Step 3: Add weights to routes
Apply an annotation at the HTTPRoute level that sets a weight for the route, which can be any 32-bit integer, including negative numbers. HTTPRoutes without the annotation are given a weight of 0
. Routes are sorted as follows:
- In descending order by weight, from highest to lowest.
- By Gateway API route precedence for routes with the same weight.
kgateway.dev/route-weight
annotation to the child HTTPRoute that you want to weight. Children do not inherit the weight of their parent HTTPRoute.Steps to weight routes:
-
Create separate HTTPRoutes for the
/anything
and/anything/a
paths.kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - "www.example.com" rules: - matches: - path: type: PathPrefix value: /anything backendRefs: - name: httpbin port: 8000 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: hello-world-a namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - "www.example.com" rules: - matches: - path: type: PathPrefix value: /anything/a backendRefs: - name: hello-world port: 80 EOF
-
Send a request to the httpbin app on the
/anything/a
path. Because the default Gateway API sorting rules give precedence to the more specific/anything/a
route, the request is served by the hello-world service instead of the httpbin service.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/a -H "host: www.example.com:8080"
curl -i localhost:8080/anything/a -H "host: www.example.com"
Example output:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 date: Thu, 13 Feb 2025 18:49:32 GMT content-length: 330 x-envoy-upstream-service-time: 4 server: envoy
Server address: 10.244.0.8:80 Server name: hello-world-669dfbd799-g4jkg Date: 29/Jul/2025:20:19:01 +0000 URI: /anything/a Request ID: dbfe6dd762fc25ae7297f2ef881ae30b
-
Add a weight of
10
to the HTTPRoute with the matching rule for the/anything
path that is served by the httpbin service, and a weight of1
to the HTTPRoute with the matching rule for the/anything/a
path that is served by the hello-world service.kubectl annotate httproute -n httpbin httpbin kgateway.dev/route-weight=10 kubectl annotate httproute -n httpbin hello-world-a kgateway.dev/route-weight=1
-
Send another request on the
/anything/a
path. Because the weight of the httpbin HTTPRoute is higher than the hello-world route, the request is served by the httpbin service instead of the hello-world service.curl -i http://$INGRESS_GW_ADDRESS:8080/anything/a -H "host: www.example.com:8080"
curl -i localhost:8080/anything/a -H "host: www.example.com"
Example output:
HTTP/1.1 200 OK access-control-allow-credentials: true access-control-allow-origin: * content-type: application/json; encoding=utf-8 date: Thu, 13 Feb 2025 18:49:32 GMT content-length: 330 x-envoy-upstream-service-time: 4 server: envoy
{ "headers": { "Accept": [ "*/*" ], "Host": [ "www.example.com" ], "User-Agent": [ "curl/8.7.1" ], "X-Envoy-Expected-Rq-Timeout-Ms": [ "15000" ], "X-Forwarded-Proto": [ "http" ], "X-Request-Id": [ "26be0bcd-d941-48f4-ac3b-d5ac288ac46f" ] } }
More resources
For more examples of weighted routes, review the following examples from the kgateway GitHub repository.
Cleanup
You can remove the resources that you created in this guide.-
Delete the extra hello-world app that you created for this tutorial.
kubectl delete deployment -n httpbin hello-world kubectl delete service -n httpbin hello-world kubectl delete httproute -n httpbin hello-world-a
-
Restore the sample httpbin HTTPRoute.
kubectl apply -f- <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: httpbin namespace: httpbin spec: parentRefs: - name: http namespace: kgateway-system hostnames: - "www.example.com" rules: - backendRefs: - name: httpbin port: 8000 EOF