> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/cd-k8s-ref/traffic-shifting-step.md).

# Kubernetes Traffic Routing step

Configure the Kubernetes Traffic Routing step to route traffic between stable and canary or stage services with Istio, SMI, or the Kubernetes Gateway API.

This topic describes the Kubernetes Traffic Routing step parameters and use cases. These configuration options are also part of the Blue Green (BG) Deployment step and the Canary Deployment step.

This feature allows you to perform east-west routing of traffic. You select a routing provider (SMI, Istio, or the Kubernetes Gateway API) and then configure one or more routes which are essentially groups of destinations and optional rules that applies for them.

## What you will learn from this topic

* How to use the Kubernetes Traffic Routing step to perform east-west traffic routing with [Istio](#istio-new-config-option).
* How to route traffic with the [Kubernetes Gateway API](#kubernetes-gateway-api-new-config-option), for clusters that use Envoy Gateway, NGINX Gateway Fabric, Contour, Cilium, GKE Gateway, or another Gateway API implementation.
* Understand the difference between the [New Config and Inherit config types](#config-type) in the Traffic Routing step.
* How to configure Istio virtual services for canary and blue green deployments.
* Understand the SMI deprecation timeline and how to migrate to Istio-based traffic routing.

{% hint style="warning" %}
**SMI end of support**

**Effective Aug 1, 2026.**

Service Mesh Interface (SMI) was officially archived by the CNCF in October 2023 and is no longer maintained upstream. Harness is deprecating support for SMI as a traffic shifting provider in Harness Kubernetes deployments.

* Existing SMI configurations are marked as deprecated in the Harness UI as of this announcement.
* New SMI-based traffic routing configurations are blocked in Kubernetes deployment configuration effective **Aug 1, 2026**.
* Use Istio as your traffic shifting provider. Harness CD provides full native Istio support for canary and blue green Kubernetes deployments with equivalent functionality. Configure it from the **Istio - New Config option** section below.

For questions, contact [Harness Support](mailto:support@harness.io).
{% endhint %}

The following is a video demo of traffic shifting in a Kubernetes deployment.

{% embed url="<https://www.loom.com/share/b1cf1db3300946b9b8fe48ae85bbfc26?sid=bef8f5d9-af26-4f24-a7ad-f244ac724572>" %}

## Name <a href="#name" id="name"></a>

Name of the step.

## Config Type <a href="#config-type" id="config-type"></a>

Specify your configuration type. The following choices are available.

* **New Config**: Select this option if you want to specify a new configuration for traffic routing in this step. If configuring this in a BlueGreen deployment step or a Canary step this option is implicitly assumed. This option will create a new resource(s).
* **Inherit**: Select this option if you want the traffic routing step to inherit a configuration from a previous Blue Green, Canary, or Traffic Routing step. This option will patch existing resources.

## Provider <a href="#provider" id="provider"></a>

Specify your routing provider. Harness supports Service Mesh Interface (SMI), Istio, and the Kubernetes Gateway API.

Each provider has some common configuration options and some provider-specific ones. The following sections list all configuration options for each provider. Review the section relevant to your provider.

## Service Mesh Interface (SMI) - New Config option <a href="#service-mesh-interface-smi-new-config-option" id="service-mesh-interface-smi-new-config-option"></a>

{% hint style="warning" %}
**Deprecated**

SMI traffic shifting is deprecated. New SMI-based configurations are blocked in Kubernetes deployment configuration effective **Aug 1, 2026**. Use Istio as your traffic shifting provider instead.
{% endhint %}

Before you begin, make sure you understand what SMI is and how it works. Go to the [SMI website](https://smi-spec.io/) to review the specification.

{% hint style="info" %}
**SMI TrafficSplit version support**

Harness supports only `specs.smi-spec.io/v1alpha3` and `specs.smi-spec.io/v1alpha4` TrafficSplit resource versions, since these are the only versions that support having rules (such as uri, method, and headers described below).
{% endhint %}

Configure the following SMI parameters.

* **Parameters**:
  * **Resource Name:** This will be used in generating a name for a kubernetes traffic routing resources. Having that mind, the name needs to be kubernetes resource name compliant.
  * **Root Service:** This should be a kubernetes service name which will be receiving incoming traffic (go to the SMI website linked above for more details). Depending on which SMI implementation you are using this value may or may not be the same as one of the destination's host (described below)
  * **Routes**:
    * **Route type**: Harness supports only the `http` route type, for http traffic.
    * **Route name**: This will be used in generating kubernetes resources so the value should kubernetes name compliant. This should also be unique within the pipeline workflow as it is used as ID for updating purposes (described below)
    * **Route Rules**: This is the way incoming requests are filtered for the configured route destinations.
      * **uri**:
        * **Value**: Enter the value that you want matched against incoming request URI.
        * **Match Type**: Used to determine how URI of the incoming request is matched against the `value` config parameter. Available to choose one of three values (`exact`, `prefix`, `regex`).
      * **method**:
        * **Value**: Used for matching the HTTP method of the incoming request.
      * **headers**:
        * **Values**:
          * **Key**: Specify the name of the request header
          * **Value**: Specify the value of the header
          * **Match Type**: Specify which kind of matching should be done against the incoming header. Available to choose one of three values (`exact`, `prefix`, `regex`).
    * **Destinations**: This is locations to which the filtered request will be routed to
      * **Host**: Should be the name of the Kubernetes service resource.

        <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><em>Only applicable when using the Blue/Green deployment step</em> - You can use placeholder <code>stable</code> and <code>stage</code> resource names. Which would effectively be replaced with the name of the stable and stage services, respectively.</p><p><em>Only applicable when using the Canary deployment step</em> You can use the placeholder <code>stable</code> and <code>canary</code> resource names. Which would effectively be replaced with the name of the stable and canary services, respectively.</p></div>
      * **Weight**: Specify the percentage of traffic that should be routed to this host. The weight should be a numeric value in range \[0 - 100].

        <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>If the total weights for all host destinations is not equal to 100, the weight values will be normalized into a percentage, and the pipeline will run with a warning.</p></div>

## Istio - New Config option <a href="#istio-new-config-option" id="istio-new-config-option"></a>

Before you configure this provider, make sure your cluster meets these requirements:

* **Istio control plane:** The Istio control plane (istiod) must be installed and running in your cluster. Go to [Istio service mesh](https://istio.io/latest/about/service-mesh/) to understand how Istio works.
* **VirtualService CRD:** The `VirtualService` custom resource definition must be installed. Harness only creates or updates a `VirtualService`. It never creates a `Gateway` or `DestinationRule`, so those resources must already exist in the cluster if you reference them by name.
* **Sidecar injection enabled on the target namespace:** Harness does not check for sidecar injection. If it is disabled, the `VirtualService` applies successfully but routes no real traffic, with no warning from Harness.

Enable sidecar injection on the target namespace before you deploy:

```bash
kubectl create namespace <namespace>
kubectl label namespace <namespace> istio-injection=enabled
```

{% hint style="info" %}
**Supported API versions**

Harness supports `networking.istio.io/v1beta1` and `networking.istio.io/v1alpha3` only. `networking.istio.io/v1` is not supported, regardless of delegate version. This is a Harness-side limitation, not an Istio-side one, so it is not resolved by changing your Istio version.
{% endhint %}

If you reference an existing `VirtualService` in your manifests, its name must exactly match the **Resource Name** configured in this step, or Harness does not merge your custom fields into it. Go to [Istio - Preserve custom VirtualService fields](#istio-preserve-custom-virtualservice-fields) to learn how field merging works.

* **Parameters**:
  * **Resource Name:** This name will be used to generate a kubernetes name for traffic resources. Hence the name needs to be kubernetes resource name compliant.
  * **Hosts:** Specify one or more host names. Hosts can be added individually using the **+Add** button or as a comma-separated list. This is specific to Istio. Go to [The Hosts field](https://istio.io/latest/docs/concepts/traffic-management/#the-hosts-field) to learn more about this Istio-specific setting.
  * **Gateways:** Specify one or more gateway names. This is specific to Istio. Go to [Gateway](https://istio.io/latest/docs/reference/config/networking/gateway/) to learn more about this Istio-specific setting.
  * **Routes**: Harness supports only the `http` route type.

    * **Route type**: Harness supports only the `http` route type, for http traffic.
    * **Route name**: This will be used in generating kubernetes resources so the value should be kubernetes name compliant. This should also be unique within the pipeline workflow as it is used as an ID for updating purposes (described below).
    * **Route Rules**: This is the way incoming requests are filtered for the configured route destinations.
      * **uri**:
        * **Value**: Enter the value that you want matched against the incoming request URI.
        * **Match Type**: Used to determine how the URI of the incoming request is matched against the `value` config parameter. Choose from one of the three available values (`exact`, `prefix`, `regex`).
      * **method**:
        * **Value**: Used for matching the HTTP method of the incoming request.
        * **Match Type**: Used to determine how the HTTP method of the incoming request is matched against the `value` config parameter. Choose from one of the three available values (`exact`, `prefix`, `regex`).
      * **headers**:
        * **Values**:
          * **Key**: Specify the name of the request header
          * **Value**: Specify the value of the header
          * **Match Type**: Specify which kind of matching should be done against the incoming header. Choose from one of the three available values (`exact`, `prefix`, `regex`).
      * **scheme**:
        * **Value**: Specify which scheme you want to match with.
        * **Match Type**: Specify which kind of matching should be done against the incoming request scheme. Choose from one of the three available values (`exact`, `prefix`, `regex`).
      * **authority**:
        * **Value**: Specify which authority you want to match with.
        * **Match Type**: Specify which kind of matching should be done against the incoming request authority. Choose from one of the three available values (`exact`, `prefix`, `regex`).
      * **port**:
        * **Value**: Specify which port you want to match the incoming request port with.
    * **Match all rules**: When enabled, all configured route rules (URI, headers, method, etc.) must match for a request to be routed to the specified destinations. When disabled, a request matching any single rule will be routed.

      <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>This setting affects how Istio evaluates multiple route rules. Go to the <a href="https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest">Istio HTTPMatchRequest specification</a> to understand how fields within a single match entry are AND'd together, while separate match entries are OR'd.</p></div>

      **Enabled (AND semantics - default):** All route rules are combined into a single HTTPMatchRequest entry. A request must satisfy all configured rules to be routed to the destination.

      **Disabled (OR semantics):** Each route rule is rendered as a separate HTTPMatchRequest entry. A request matching any configured rule will be routed to the destination.

      <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>Using OR semantics (<strong>Match all rules</strong> disabled) with path-based and header-based rules can cause issues. For example, any request hitting the URI prefix will be routed to canary regardless of the cookie header, potentially causing you to receive a mix of stable and canary resources (CSS, JavaScript, etc.), which may break the application.</p></div>
    * **Rewrite Rule**: A rewrite rule in a traffic shifting step refers to modifying the incoming request’s path or URL before it is forwarded to the backend service.

      <figure><img src="https://3694223630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fy1JhZ4oKIppwY7d5AhPj%2Fuploads%2Fgit-blob-166ab7048c7319f5f5ca8d3bb1f0fb70a2fc3708%2Fadd-rewrite-rule.png?alt=media" alt="The Rewrite Rule field in the Traffic Routing step"><figcaption><p>Click to view full size image</p></figcaption></figure>

    The following is a sample rewrite rule for reference. Note that each rule must begin with **`rewrite:`**.

    * **Destinations**: The filtered requests will be routed to these locations.
      * **Host**: Should be the name of the Kubernetes service resource.

        <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><em>Only applicable when using the Blue/Green deployment step</em> - You can use placeholder <code>stable</code> and <code>stage</code> resource names. Which would effectively be replaced with the name of the stable and stage services, respectively.</p><p><em>Only applicable when using the Canary deployment step</em> You can use the placeholder <code>stable</code> and <code>canary</code> resource names. Which would effectively be replaced with the name of the stable and canary services, respectively.</p></div>

        * **Delegate Virtual Service Support**

        With [delegate virtual service](https://istio.io/latest/docs/reference/config/networking/virtual-service/#Delegate) support, the **Host** field can be left empty when using custom rewrite logic. This allows for more dynamic traffic routing configurations, enabling users to rewrite traffic-routing logic to fit their deployment strategies. To configure this, you need to check the **Delegate Service** option, which creates a delegate virtual service. When this option is checked, the **Host** field will be left empty.

        This is available for **Canary Deployment, K8s Traffic routing, K8s Blue Green Deploy**.

        ![The Delegate Service option in the Istio traffic routing configuration](https://3694223630-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fy1JhZ4oKIppwY7d5AhPj%2Fuploads%2Fgit-blob-8599a253de84595d0e336930d04761b8d4d6fef8%2Fre-write-istio-canary.png?alt=media)
      * **Weight**: Specify the percentage of traffic that should be routed to this host. The weight should be a numeric value in range \[0 - 100].

        <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>If the total weights for all host destinations is not equal to 100, the weight values will be normalized into a percentage, and the pipeline will run with a warning.</p></div>

## Istio - Preserve custom VirtualService fields <a href="#istio-preserve-custom-virtualservice-fields" id="istio-preserve-custom-virtualservice-fields"></a>

Harness preserves custom VirtualService configurations (such as `headers`, `fault`, `timeout`, `retries`, `corsPolicy`) during canary and blue green deployments when a VirtualService is provided in the manifest. Without this capability, Harness overwrites these fields when it applies traffic routing changes.

This is useful for deployments that require VirtualService-level header manipulation, fault injection, or other advanced Istio configurations to remain intact throughout the deployment lifecycle.

{% hint style="info" %}
This feature is behind the feature flag `CDS_ISTIO_VS_MERGE_IN_TRAFFIC_ROUTING`. Contact [Harness Support](mailto:support@harness.io) to enable the feature. This feature requires minimum versions of ng-manager `1.136.0`, pipeline-service `1.176.0`, and Delegate `26.03.88800`.
{% endhint %}

{% hint style="info" %}
This feature applies only to **Istio** traffic routing. SMI is not supported.
{% endhint %}

**How it works**

When Traffic Routing is configured in your canary or blue green deployment:

1. **Match:** Harness looks for a VirtualService in your manifest that matches the **resource name** specified in your Traffic Routing configuration.
2. **Merge:** If found, Harness merges only the traffic routing changes (weights, destinations) into your VirtualService while preserving all other fields.
3. **Fallback:** If no matching VirtualService is found, Harness creates one from scratch (existing behavior).

**Requirements**

The following table lists the requirements for merging VirtualService fields.

| Requirement                        | Description                                                                                                                             |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **VirtualService name must match** | The `resourceName` in your Traffic Routing configuration must exactly match the `metadata.name` of your VirtualService in the manifest. |
| **Istio only**                     | This feature only works with Istio VirtualServices.                                                                                     |

**Important behavior**

**Single route matching**

If your VirtualService has **one HTTP route** and your step config also defines **one route**, they are merged automatically, and no route naming is required.

**Multiple routes**

If your VirtualService has **multiple HTTP routes**, Harness matches routes by the `name` field (`spec.http[].name`).

{% hint style="warning" %}
If your routes do not have names and you have multiple routes, step-defined routes are **appended** instead of merged. This may cause unexpected behavior. Always use named routes when you have multiple HTTP routes.
{% endhint %}

**Hosts and gateways**

If your step config specifies `hosts` or `gateways`, they **overwrite** your manifest values. Leave these empty in your step config to preserve your manifest values.

**Rollback**

On rollback, Harness re-applies the last successful release's manifests, which includes your original VirtualService with all custom fields intact.

**Example**

<details>

<summary>VirtualService manifest (before deployment)</summary>

```yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-service-vs
  annotations:
    harness.io/direct-apply: "true"
spec:
  gateways:
    - my-service-gw
  hosts:
    - "*"
  http:
    - name: primary-route
      headers:
        request:
          set:
            X-Request-ID: trace-123
        response:
          set:
            X-Response-Source: stable
      fault:
        delay:
          percentage:
            value: 0.1
          fixedDelay: 5s
      timeout: 30s
      retries:
        attempts: 3
        perTryTimeout: 10s
        retryOn: gateway-error,connect-failure
      corsPolicy:
        allowOrigins:
          - exact: https://example.com
        allowMethods:
          - GET
          - POST
        maxAge: 24h
      match:
        - uri:
            prefix: /api/v1
      route:
        - destination:
            host: my-service
            port:
              number: 8080
            subset: stable
          headers:
            response:
              remove:
                - X-Server-Internal
          weight: 100
```

</details>

<details>

<summary>VirtualService after Canary Deploy</summary>

```yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-service-vs
  annotations:
    harness.io/direct-apply: "true"       # Preserved
spec:
  gateways:
    - my-service-gw
  hosts:
    - "*"
  http:
    - name: primary-route
      headers:                             # Preserved
        request:
          set:
            X-Request-ID: trace-123
        response:
          set:
            X-Response-Source: stable
      fault:                               # Preserved
        delay:
          percentage:
            value: 0.1
          fixedDelay: 5s
      timeout: 30s                         # Preserved
      retries:                             # Preserved
        attempts: 3
        perTryTimeout: 10s
        retryOn: gateway-error,connect-failure
      corsPolicy:                          # Preserved
        allowOrigins:
          - exact: https://example.com
        allowMethods:
          - GET
          - POST
        maxAge: 24h
      match:
        - uri:
            prefix: /api/v1
      route:
        - destination:
            host: my-service
            port:
              number: 8080
            subset: stable                 # Preserved
          headers:                         # Preserved
            response:
              remove:
                - X-Server-Internal
          weight: 80                       # Updated from step config
        - destination:
            host: my-service-canary
            port:
              number: 8080
          weight: 20                       # Added from step config
```

</details>

## Kubernetes Gateway API - New Config option <a href="#kubernetes-gateway-api-new-config-option" id="kubernetes-gateway-api-new-config-option"></a>

{% hint style="info" %}
This feature is behind the feature flag `CDS_K8S_GATEWAY_TRAFFIC_ROUTING`. Contact [Harness Support](mailto:support@harness.io) to enable the feature.
{% endhint %}

The [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io/) is the Kubernetes project's successor to Ingress. Use this provider if your team has standardized on the Gateway API through any implementation, such as Envoy Gateway, NGINX Gateway Fabric, Contour, Cilium, or GKE Gateway. Before this provider existed, these teams could not use Harness's weighted traffic shifting without maintaining a parallel mesh resource or scripting the shift outside the pipeline.

Harness creates and updates an `HTTPRoute` resource attached to a Gateway that you already own. Harness never creates or manages the Gateway or the `GatewayClass`.

You author the traffic routing configuration once, and Harness uses it across four deployment paths: the standalone Kubernetes Traffic Routing step (also how Rolling deployments shift traffic), the Canary Deployment step, the Progressive Canary strategy, and the Blue Green (BG) Deployment step. For Progressive Canary, weights advance through checkpoints and roll back down on failure. For Blue Green, the swap step moves all traffic to the promoted version.

Before you configure this provider, make sure your cluster meets these requirements:

* **Gateway API CRDs installed:** The Gateway API CRDs do not ship with any Kubernetes distribution by default, and `gateway.networking.k8s.io/v1` must be served.
* **An existing Gateway:** A Gateway must already exist in the cluster, with a Gateway API implementation (controller) running, such as Envoy Gateway, NGINX Gateway Fabric, Contour, Cilium, or GKE Gateway.
* **Delegate permissions:** The delegate needs `create`, `get`, and `patch` permissions on `httproutes.gateway.networking.k8s.io` in the release namespace. A delegate role scoped to mesh resources only does not have this.
* **Cross-namespace attachment pre-authorized:** If the Gateway lives in a different namespace than the route, the platform team must configure a `ReferenceGrant` on the Gateway side. Harness never creates this, by design. Without it, Harness creates the route, but the route never attaches to the Gateway.

{% hint style="info" %}
**Not supported in this release**

The Kubernetes Gateway API provider supports only the `HTTPRoute` resource kind. Harness does not support `GRPCRoute`, `TCPRoute`, `TLSRoute`, or `UDPRoute`, and it does not support attaching a route to a Service for mesh (east-west) routing. Query-parameter match rules are also not supported, for the Kubernetes Gateway API or any other traffic routing provider.
{% endhint %}

{% hint style="info" %}
**Supported API versions**

Harness supports `gateway.networking.k8s.io/v1` and `gateway.networking.k8s.io/v1beta1`.
{% endhint %}

Harness maps this configuration onto the `HTTPRoute` spec directly: each route becomes one rule, each destination becomes one weighted backend reference, and each `gateways` entry becomes a parent reference on the resource.

* **Parameters**:
  * **Resource Name:** This name is used to generate the `HTTPRoute` resource name, so it needs to be Kubernetes resource name compliant.
  * **Host Names:** Specify one or more hostnames. Host names can be added individually using the **+Add** button or as a comma-separated list.
  * **Gateways:** Specify one or more gateways for the `HTTPRoute` to attach to. For each gateway, enter a **Name**, and optionally a **Namespace** (defaults to the release namespace) and a **Section Name**, which is the name of a listener on the Gateway.
  * **Routes**: Harness supports only the `http` route type.
    * **Route type**: Harness supports only the `http` route type, for http traffic.
    * **Route name**: This will be used in generating kubernetes resources so the value should be kubernetes name compliant. Route names must be a lowercase RFC 1123 subdomain (1-253 characters); Harness validates this rather than silently rewriting the name. This should also be unique within the pipeline workflow as it is used as an ID for updating purposes (described below).
    * **Route Rules**: This is the way incoming requests are filtered for the configured route destinations. The Kubernetes Gateway API provider supports only `uri`, `method`, and `headers` rules.

      * **uri**:
        * **Value**: Enter the value that you want matched against the incoming request URI.
        * **Match Type**: Used to determine how the URI of the incoming request is matched against the `value` config parameter. Choose from one of the three available values (`exact`, `prefix`, `regex`).
      * **method**:
        * **Value**: Used for matching the HTTP method of the incoming request.
      * **headers**:
        * **Values**:
          * **Key**: Specify the name of the request header.
          * **Value**: Specify the value of the header.
          * **Match Type**: Specify which kind of matching should be done against the incoming header. Choose from one of the three available values (`exact`, `prefix`, `regex`). The Gateway API has no native prefix header match, so Harness translates a prefix match into an equivalent regex.

      <div data-gb-custom-block data-tag="hint" data-style="warning" class="hint hint-warning"><p>Harness rejects <code>scheme</code>, <code>authority</code>, and <code>port</code> match rules for the Kubernetes Gateway API provider when the pipeline runs, because the Gateway API has no per-rule equivalent for them. Express hostname constraints in the <strong>Host Names</strong> field, and choose the listener with the gateway's <strong>Section Name</strong> field instead.</p></div>

      <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>Regex support is implementation-dependent. The Gateway API does not require a Gateway API implementation to support regex matching, and implementations that support it may use different regex dialects. This also applies to prefix header matches, since Harness implements them as a regex. Use <code>exact</code> or <code>prefix</code> URI matches where you can.</p></div>
    * **Match all rules**: When enabled, all configured route rules (URI, headers, method) must match for a request to be routed to the specified destinations. When disabled, a request matching any single rule is routed. Go to [Match all rules](#istio-new-config-option) above to review the AND/OR semantics, which apply the same way to the Kubernetes Gateway API provider.
    * **Rewrite Rule**: A rewrite rule modifies the incoming request's path or URL before Harness forwards it to the backend service. This works the same way as the Istio rewrite rule described above.
    * **Destinations**: The filtered requests are routed to these locations.
      * **Host**: Should be the name of the Kubernetes service resource.

        <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><em>Only applicable when using the Blue/Green deployment step</em> - You can use placeholder <code>stable</code> and <code>stage</code> resource names. Which would effectively be replaced with the name of the stable and stage services, respectively.</p><p><em>Only applicable when using the Canary deployment step</em> You can use the placeholder <code>stable</code> and <code>canary</code> resource names. Which would effectively be replaced with the name of the stable and canary services, respectively.</p></div>
      * **Port:** Required for the Kubernetes Gateway API provider. The `HTTPRoute` resource requires a port on every backend reference, so Harness validates this when the pipeline runs rather than letting the cluster reject the resource mid-deploy.
      * **Weight**: Specify the percentage of traffic that should be routed to this host. The weight should be a numeric value in range \[0 - 100].

        <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p>If the total weights for all host destinations is not equal to 100, the weight values will be normalized into a percentage, and the pipeline will run with a warning. Destinations left without a weight share the remaining percentage equally.</p></div>

{% hint style="info" %}
**Resource limits**

The Gateway API caps an `HTTPRoute` at 16 rules, 64 matches per rule, and 16 backend references per rule. Configurations beyond these limits are rejected when the pipeline runs. If you hit this limit, split the configuration across multiple traffic-routing steps.
{% endhint %}

### Move an existing pipeline to the Kubernetes Gateway API

Moving a pipeline from SMI or Istio to the Kubernetes Gateway API does not require a migration or data change, and it does not change the behavior of pipelines that stay on their current provider. To move a pipeline, switch the **Provider** to **gateway**, use **Host Names** instead of **Hosts**, give each **Gateways** entry a name (and a namespace or section name, if needed) instead of a bare string, add a **Port** to every destination, and remove any `scheme`, `authority`, or `port` match rules. Routes, rules, destinations, weights, and the `stable`, `stage`, and `canary` host placeholders carry over unchanged. Harness does not convert an existing SMI or Istio resource into an `HTTPRoute`.

## SMI, Istio, and Kubernetes Gateway API - Inherit option

This option provides a way to update an existing traffic routing configuration destination's weights. With this step configuration there are two logical parts. First one is to configure the `Route Name` and second one is to configure destination(s) which you want to update. The route name is an ID used to identify which traffic routing resource needs to be updated. The value of the route name should match one of the route names defined during the `New config` step configuration. You can configure one or more destinations and their weights. You should also be aware that in case you configure lower number of destinations then the route contains, the update of the weights will occur, but it will respect \[0-100] boundaries.

Example: In original setup you have configured 3 destinations for a route:

```
Destination1 -> Host: svc1, Weight: 60
Destination2 -> Host: svc2, Weight: 30
Destination3 -> Host: svc3, Weight: 10
```

In the update step you configure to update only one destination for that same route. Consider updating destination `svc1` to set its weight to `40`

This will lead to having the remaining 60 (max limit of 100 - configured update weight of 40) to be split amongst the remaining destinations, but keeping the same ratio they have amongst themselves. Meaning the result would be:

```
Destination1 -> Host: svc1, Weight: 40
Destination2 -> Host: svc2, Weight: 45
Destination3 -> Host: svc3, Weight: 15
```

## Configuration examples <a href="#configuration-examples" id="configuration-examples"></a>

The following examples show the YAML for an Istio traffic routing step, an SMI traffic routing step, and a Kubernetes Gateway API traffic routing step.

### Istio service mesh configuration <a href="#istio-service-mesh-configuration" id="istio-service-mesh-configuration"></a>

The following is an example of an Istio service mesh traffic routing step. It takes all traffic coming from gateway `testgateway` with host `test.com`. It filters incoming requests that have URI `/login`, HTTP method `POST`, and header `X-Request` with value `authxx`. With `matchAllConditions` set to `true`, a request must satisfy all three criteria to be routed. Traffic is split between pods behind `svc1` and `svc2` at a 65/35 ratio. The resource created is a `VirtualService` named `istio-vs-k8s-res`.

<details>

<summary>Istio Traffic Routing YAML example</summary>

```yaml
  - step:
      identifier: K8sTrafficRoutingConfig
      type: K8sTrafficRouting
      name: config
      spec:
        type: config
        trafficRouting:
          provider: istio
          spec:
            name: istio-vs-k8s-res
            hosts:
              - test.com
            gateways:
              - testgateway
            routes:
              - route:
                  type: http
                  name: route1
                  rules:
                    - rule:
                        type: uri
                        spec:
                          value: /login
                          matchType: prefix
                    - rule:
                        type: method
                        spec:
                          value: POST
                    - rule:
                        type: headers
                        spec:
                          values:
                            - value: authxx
                              key: X-Request
                              matchType: prefix
                  destinations:
                    - destination:
                        host: svc1
                        weight: 65
                    - destination:
                        host: svc2
                        weight: 35
          matchAllConditions: true
```

</details>

### SMI service mesh configuration <a href="#smi-service-mesh-configuration" id="smi-service-mesh-configuration"></a>

The following is an example of an SMI service mesh traffic routing step that takes all the traffic coming into service `svc1`. It filters all incoming request that have URI `/login` with HTTP method `POST` and header `X-Request` with value `authxx`. This request will be split between PODs which are behind two service `svc1` and `svc2` in ratio 65 to 35, respectively. The resource created would be a `TrafficSplit` with name `smi-traffic-split-res-route1` and `HTTPRouteGroup` with name `smi-traffic-split-res-route1-http-rule`

```yaml
  - step:
      identifier: K8sTrafficRoutingConfig
      type: K8sTrafficRouting
      name: config
      spec:
        type: config
        trafficRouting:
          provider: smi
          spec:
            rootService: svc1
            name: smi-traffic-split-res
            routes:
              - route:
                  type: http
                  rules:
                    - rule:
                        type: uri
                        spec:
                          value: /login
                          matchType: prefix
                    - rule:
                        type: method
                        spec:
                          value: POST
                    - rule:
                        type: headers
                        spec:
                          values:
                            - value: authxx
                              key: X-Request
                              matchType: prefix
                  name: route1
                  destinations:
                    - destination:
                        host: svc1
                        weight: 65
                    - destination:
                        host: svc2
                        weight: 35
```

### Kubernetes Gateway API configuration <a href="#kubernetes-gateway-api-configuration" id="kubernetes-gateway-api-configuration"></a>

The following is an example of a Kubernetes Gateway API traffic routing step. It attaches to the `http` listener on gateway `my-gateway` in namespace `infra`, for host `app.example.com`. It filters incoming requests with a URI prefix of `/api`. With `matchAllRules` set to `true`, a request must satisfy all configured rules to be routed. Traffic is split between the `stable` and `canary` services at a 90/10 ratio. The resource created is an `HTTPRoute` named `httproute-<+service.name>`.

<details>

<summary>Kubernetes Gateway API Traffic Routing YAML example</summary>

```yaml
  - step:
      identifier: K8sTrafficRoutingConfig
      type: K8sTrafficRouting
      name: config
      spec:
        type: config
        trafficRouting:
          provider: gateway
          spec:
            name: httproute-<+service.name>
            gateways:
              - name: my-gateway
                namespace: infra
                sectionName: http
            hostnames:
              - app.example.com
            routes:
              - route:
                  name: primary
                  type: http
                  matchAllRules: true
                  rules:
                    - rule:
                        type: uri
                        spec:
                          matchType: prefix
                          value: /api
                  destinations:
                    - destination:
                        host: stable
                        port: 8080
                        weight: 90
                    - destination:
                        host: canary
                        port: 8080
                        weight: 10
```

</details>

## Advanced <a href="#advanced" id="advanced"></a>

Go to the following topics to configure advanced settings.

* [Delegate Selector](https://developer.harness.io/harness-platform/use-harness-platform/delegates/delegate/manage-delegates/select-delegates-with-selectors)
* [Conditional Execution](https://developer.harness.io/harness-platform/use-harness-platform/pipelines/step-skip-condition-settings)
* [Failure Strategy](https://developer.harness.io/harness-platform/use-harness-platform/pipelines/failure-handling/define-a-failure-strategy-on-stages-and-steps)
* [Looping Strategy](https://developer.harness.io/harness-platform/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism)
* [Policy Enforcement](https://developer.harness.io/harness-platform/use-harness-platform/governance/policy-as-code/harness-governance-overview)

## Troubleshooting <a href="#troubleshooting" id="troubleshooting"></a>

<details>

<summary>Traffic routing fails with <code>CRD specification wasn't found for networking resource in the cluster</code></summary>

This means Istio is not installed in the target cluster, or the `VirtualService` CRD is missing. The step fails with an error similar to:

```
Failed to execute traffic routing
Supported versions for resource type [networking] are: [networking.istio.io/v1beta1, networking.istio.io/v1alpha3]
CRD specification wasn't found for networking resource in the cluster
```

Install the Istio control plane and confirm the CRD is present before you retry:

```bash
kubectl get crd | grep istio
```

Go to [Istio - New config option](#istio-new-config-option) to review the full prerequisite list.

</details>

<details>

<summary>Headers are still being removed from my VirtualService during canary or blue green deployment with Istio traffic routing</summary>

Verify the resourceName in your Traffic Routing configuration exactly matches your VirtualService's metadata.name. Check deployment logs for 'Found existing VirtualService \[name] in manifest. Merging with step-level traffic routing config.' If this message is absent, the merge did not occur. Also confirm you are using Istio, not SMI.

</details>

<details>

<summary>Unexpected route behavior with multiple HTTP routes in VirtualService during Istio traffic routing merge</summary>

Add a name field to each HTTP route in your VirtualService for predictable merging. Without names, step-defined routes are appended instead of merged.

</details>

<details>

<summary>Example VirtualService with named routes</summary>

```yaml
spec:
  http:
    - name: api-route
      headers: { ... }
      route: [ ... ]
    - name: web-route
      headers: { ... }
      route: [ ... ]
```

</details>

<details>

<summary>Destinations with same host but different subsets are collapsed into a single destination</summary>

Use type: inherit instead of type: config to preserve existing VirtualService destinations when subset-based splitting is required; alternatively, split each subset into its own named route with a single destination. Tracked in CDS-124991.

</details>

The following example shows a Harness route configuration that triggers this deduplication.

<details>

<summary>Example of affected configuration</summary>

```yaml
routes:
  - route:
      type: http
      name: my-route
      destinations:
        - destination:
            host: my-service
            subset: canary
            weight: 25
        - destination:
            host: my-service
            subset: stable
            weight: 75
```

In this example, both destinations share the same host (`my-service`). The step collapses them into a single destination with `weight: 100` and no subset field.

</details>

<details>

<summary>Traffic is routed to canary when only one of multiple route rules matches (URI or header, not both)</summary>

Enable the 'Match all rules' checkbox in your Traffic Routing configuration. When disabled, Istio applies OR semantics, meaning any matching rule routes traffic to canary. When enabled (AND semantics), all configured rules must match. This prevents issues where you receive mixed stable/canary resources that break the application.

</details>

<details>

<summary>Kubernetes Gateway API traffic routing fails with a CRD not found error</summary>

This means the Gateway API CRDs are not installed in the target cluster, or `gateway.networking.k8s.io/v1` is not served. Install the Gateway API CRDs and confirm the version is served before you retry:

```bash
kubectl get crd | grep gateway.networking.k8s.io
```

Go to [Kubernetes Gateway API - New config option](#kubernetes-gateway-api-new-config-option) to review the full prerequisite list.

</details>

<details>

<summary>Deploy succeeds but no traffic shifts with the Kubernetes Gateway API provider</summary>

Harness created the `HTTPRoute`, but it never attached to the Gateway. This is usually a typo in the gateway's **Name** or **Section Name**, or a missing cross-namespace authorization when the Gateway lives in a different namespace than the route. Inspect the route's status conditions in the cluster to confirm whether it attached.

</details>

<details>

<summary>A match rule is rejected when the pipeline runs with the Kubernetes Gateway API provider</summary>

This happens with `scheme`, `authority`, or `port` match rules, because the Gateway API has no per-rule equivalent for them. Express hostname constraints in the **Host Names** field, and choose the listener with the gateway's **Section Name** field instead.

</details>

<details>

<summary>A destination is rejected with the Kubernetes Gateway API provider</summary>

The destination is missing a **Port**. Unlike SMI and Istio, the Kubernetes Gateway API provider requires a port on every destination. Add a **Port** to each destination and retry.

</details>

<details>

<summary>A route name is rejected with the Kubernetes Gateway API provider</summary>

The route name is not a valid lowercase RFC 1123 subdomain (1-253 characters). Rename the route.

</details>

<details>

<summary>A regex match never fires with the Kubernetes Gateway API provider</summary>

The Gateway API does not require implementations to support regex matching, and implementations that do support it may use a different regex dialect. This also applies to prefix header matches, since Harness implements them as a regex. Check your Gateway API implementation's documentation, and prefer `exact` or `prefix` URI matches where you can.

</details>

<details>

<summary>A warning about matching a rule by position during a weight update with the Kubernetes Gateway API provider</summary>

This means the cluster's Gateway API CRD bundle is older than v1.2 and drops rule names from the applied `HTTPRoute`. Upgrade the CRD bundle, then verify the weights landed on the intended rule.

</details>

## Next steps

* [Kubernetes canary deployments](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/kubernetes-executions/create-a-kubernetes-canary-deployment.md)
* [Kubernetes blue-green deployments](/continuous-delivery/use-continuous-delivery/deploy-services-on-different-platforms/kubernetes/kubernetes-executions/create-a-kubernetes-blue-green-deployment.md)
* [CD best practices](/continuous-delivery/troubleshooting-and-resources/cd-best-practices.md)

{% @harness-feedback/feedback %}
