Skip to content

Prerequisites

Skew protection supports Kubernetes and ECS, with different routing prerequisites. Kubernetes requires a Gateway API controller. ECS requires a configured ALB listener and supports query pinning only.

Skew protection is disabled by default. Enable it via the PLT_FEATURE_SKEW_PROTECTION environment variable or the Helm value services.icc.features.skew_protection.enable:

Terminal window
PLT_FEATURE_SKEW_PROTECTION=true

When disabled, ICC creates no version-aware HTTPRoute or ALB listener rules. A Gateway controller is required only for Kubernetes skew protection.

The Gateway API is not built into Kubernetes. The Custom Resource Definitions (CRDs) must be installed separately:

Terminal window
kubectl apply --server-side=true -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.1/standard-install.yaml

This installs the CRDs only (GatewayClass, Gateway, HTTPRoute) — no controller is deployed.

A Gateway API-compliant controller must be deployed in the cluster to process Gateway and HTTPRoute resources.

What the controller must support depends on which pinning mode you use. ICC defaults to query (see Configuration).

Required in both modes:

FeatureConformance LevelRequired For
HTTPRoute with backendRefsCoreRouting to versioned Services
headers match with Exact typeCorex-deployment-id header matching

Required by query pinning (PLT_SKEW_DEFAULT_ROUTING_MODE=query, the default):

FeatureConformance LevelRequired For
queryParams match with Exact typeExtendedMatching ?dpl=<version> on the request

Required by cookie pinning (PLT_SKEW_DEFAULT_ROUTING_MODE=cookie):

FeatureConformance LevelRequired For
ResponseHeaderModifier filterExtendedSetting Set-Cookie on responses
headers match with RegularExpression typeImplementation-specificReading the __plt_dpl cookie back off the request

This table covers Kubernetes Gateway API controllers only. On AWS it therefore describes EKS, where a controller reconciles HTTPRoute resources; it says nothing about ECS, which does not use the Gateway API at all.

This table is a dated snapshot of the Gateway API v1.3.0 conformance reports, in which each project declares the features it implements. Query mode is reported as HTTPRouteQueryParamMatching; the cookie column reports HTTPRouteResponseHeaderModification, which is the Set-Cookie half of cookie pinning. Check the current conformance reports before choosing a controller because versions and feature support can change.

ControllerVersion in reportQueryCookie
Envoy Gatewayv1.5.0YesYes
Istio1.26.1YesYes
Contourv1.33.0YesYes
CiliummainYesYes
Traefikv3.5YesYes
kgatewayv2.1.0YesYes
Gloo Gatewayv1.20.13YesYes
NGINX Gateway Fabric2.0.0, 2.1.0, 2.2.0YesYes
Linkerd2.18YesYes
Airlock Microgateway4.6.0 - 5.1.0YesYes
Alibaba Cloud Service Mesh1.27YesYes
Kong Operator (expressions router)v2.0.6YesYes
Kong Operator (traditional_compatible router)v2.0.6NoYes
GKE Gateway1.33.4-gke.1134000NoYes
AWS Load Balancer Controllerv2.17.0, v3.0.0NoNo
Gravitee4.8.5Not reportedNot reported

Platformatic verifies both modes against Envoy Gateway v1.4.2, which is the controller used by the reference EKS deployment.

A “Yes” in the Cookie column confirms the controller can set the cookie. Cookie mode also needs a RegularExpression header match to read it back, which has no conformance feature name, so confirm that from your controller’s documentation. This only decides the outcome for controllers whose Query column is No, since anywhere both are available query mode is the recommended default.

ECS does not use the Gateway API. ICC sends a provider-neutral route plan to Machinist, which creates host-scoped rules on an ALB listener.

Before enabling skew protection on ECS:

  1. Set ICC’s PLT_MACHINIST_PROVIDER=ecs, set Machinist’s PLT_PROVIDER=ecs, and configure Machinist with PLT_ECS_LISTENER_ARN.
  2. Give Machinist permission to describe the listener and load balancer and to describe, create, tag, and delete listener rules and target groups.
  3. Use hostname-based routing. ECS route application rejects an application without a hostname because ALB cannot remove an external path prefix.
  4. Use query routing. ALB cannot add the response cookie required by cookie mode.
  5. Give every version its own ECS service and target group. An externally created service must be attached to its target group when the service is created. ICC-managed deployments create both through Machinist.
  6. For ICC-managed deployments, configure PLT_ECS_CLOUD_MAP_NAMESPACE_ID and the ECS, Cloud Map, iam:PassRole, registry-secret, and load-balancer permissions required by the workload actuator.
  7. Ensure Prometheus discovers and scrapes every version. ECS per-version expiry requires @platformatic/watt-extra 1.16.0 or later so metrics carry the deploymentVersion label.
  8. Configure each target group’s health check against Watt’s dedicated health endpoint on port 9090, normally HTTP:9090/ready. Do not health-check the application listener on port 3042: those requests are application ingress metrics, so ALB probes would appear as permanent traffic and prevent both HTTP and workflow versions from expiring early. ICC-managed ECS deployments created by Machinist use the dedicated health port automatically.

ALB listener-rule and target-group quotas bound the number of applications and simultaneously live versions. Check those quotas for the selected listener and region before setting a high per-application version limit.

Version labels are matched differently on ALB

Section titled “Version labels are matched differently on ALB”

The Gateway API and ALB do not agree on what counts as a match for the pinning parameter, so the same version labels behave differently on EKS and ECS.

Kubernetes Gateway APIALB (ECS)
CaseExact: ?dpl=V1 does not match version v1Case insensitive: ?dpl=V1 matches v1
* and ? in a labelLiteral charactersWildcards: * matches any run of characters, ? matches one

The ICC deploy API rejects these ambiguous values, but a customer-managed reactive registration can still present two live labels that differ only in case. On EKS each is pinned separately; on ECS their pinning rules are indistinguishable and the lower priority wins, so a request pinned to one can be served by the other. The same applies to a manually supplied label containing * or ?, which ALB can match against several versions.

Use version labels that differ by more than case, and avoid * and ? in them. Labels produced by a deploy pipeline normally satisfy this already; it is worth checking if you set them by hand.

Traffic-based expiry reads per-version request rates from Prometheus, and how a version is identified depends on the platform.

On Kubernetes, a version is a distinct workload, and ICC selects its instances by joining kube_pod_labels on app.kubernetes.io/instance. This needs nothing from the application beyond exposing its metrics, but it does require that label to be set — see Deploying Versioned Applications.

On ECS, there is no such join. The application reports its own version as a deploymentVersion label on its metrics, which @platformatic/watt-extra 1.16.0 or later emits from the version ICC assigns at registration.

In both cases the metrics read are the application’s own Node.js metrics; the Kubernetes join only decides which instances belong to the version.

Infrastructure probes must not enter those application metrics. On ECS, point the ALB target-group health check at Watt’s readiness endpoint on port 9090, not at an application route on port 3042. A target group is checked by every enabled ALB node, so even an unused version otherwise reports non-zero RPS. Kubernetes readiness and liveness probes should use the same dedicated health port rather than the application port.

Kubernetes Gateway and GatewayClass Resources

Section titled “Kubernetes Gateway and GatewayClass Resources”

The cluster operator must create a GatewayClass and Gateway resource before ICC can manage HTTPRoute resources. This is standard Gateway API setup:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: platformatic
spec:
# controllerName varies by controller — this example uses Envoy Gateway
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: platform-gateway
namespace: platformatic
spec:
gatewayClassName: platformatic
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: platform-tls
- name: http
protocol: HTTP
port: 80

ICC references this Gateway in the parentRefs of each managed HTTPRoute. The Gateway resource is auto-discovered — no additional configuration is needed unless your cluster has multiple Gateways.

Machinist’s Kubernetes service account must have permissions to manage HTTPRoute resources. If you are using the Platformatic Helm chart, these permissions are included automatically. For manual installations, add the following to Machinist’s ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: machinist-gateway-api
rules:
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["httproutes"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gateways"]
verbs: ["get", "list"]

Gateway API v1.5.1 is the current supported release at the time of writing. Use the Gateway API project’s compatibility policy to select a release supported by your Kubernetes version and controller.

Note that the ICC Helm chart requires Kubernetes 1.30 or newer regardless (enforced by its kubeVersion constraint), so 1.30 is the effective floor for any ICC installation.