Warning
You are currently viewing v0.15 of the documentation and it is not the latest. For the most recent documentation, kindly click here.
Migrate from HTTPScaledObject to InterceptorRoute
How to migrate existing HTTPScaledObject resources to the InterceptorRoute API
This guide shows you how to migrate existing HTTPScaledObject (v1alpha1) resources to the InterceptorRoute (v1beta1) API.
kubectl access to the clusterHTTPScaledObject resources to migrateThe InterceptorRoute API replaces HTTPScaledObject with a cleaner separation of concerns.
InterceptorRoute controller no longer creates a KEDA ScaledObject for you.
You create and manage it yourself, with access to all supported KEDA ScaledObject fields.rules[] structure lets you define multiple routing rules on a single resource, each with its own hosts, paths, and headers.service and port/portName.
Workload fields (name, apiVersion, kind) move to the ScaledObject where they belong.request timeout controls the full request lifecycle.
conditionWait is renamed to readiness for clarity.targetValue is required:
On HTTPScaledObject, targetValue defaulted to 100 when omitted.
On InterceptorRoute, targetValue must be set explicitly.
If your HTTPScaledObject relied on the default, add targetValue: 100 (or your preferred value) when migrating.replicas, targetPendingRequests, scaledownPeriod, and initialCooldownPeriod are configured on the KEDA ScaledObject directly.The migration is a direct field-by-field conversion. The Apply the migration without downtime section below shows how to perform the switchover without interrupting traffic.
apiVersion: http.keda.sh/v1alpha1
kind: HTTPScaledObject
metadata:
name: my-app
namespace: default
spec:
hosts:
- app.example.com
pathPrefixes:
- /api
- /health
headers:
- name: X-Custom-Header
value: "my-value"
scaleTargetRef:
name: my-app
kind: Deployment
apiVersion: apps/v1
service: my-app-svc
port: 8080
coldStartTimeoutFailoverRef:
service: fallback-svc
port: 8080
timeoutSeconds: 45
replicas:
min: 0
max: 10
scalingMetric:
concurrency:
targetValue: 50
timeouts:
conditionWait: 30s
responseHeader: 10s
scaledownPeriod: 120
The single HTTPScaledObject becomes two resources: an InterceptorRoute for routing and an independently managed ScaledObject for scaling.
InterceptorRoute:
apiVersion: http.keda.sh/v1beta1
kind: InterceptorRoute
metadata:
name: my-app
namespace: default
spec:
target:
service: my-app-svc
port: 8080
coldStart:
fallback:
service:
name: fallback-svc
port: 8080
rules:
- hosts:
- app.example.com
paths:
- value: /api
- value: /health
headers:
- name: X-Custom-Header
value: "my-value"
scalingMetric:
concurrency:
targetValue: 50
timeouts:
readiness: 45s
responseHeader: 10s
ScaledObject:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: my-app
namespace: default
spec:
scaleTargetRef:
name: my-app
kind: Deployment
apiVersion: apps/v1
minReplicaCount: 0
maxReplicaCount: 10
cooldownPeriod: 120
triggers:
- type: external-push
metadata:
scalerAddress: "keda-add-ons-http-external-scaler.keda:9090"
interceptorRoute: "my-app"
Note the following conversions:
scaleTargetRef becomes target with only service and port/portName.hosts, pathPrefixes, and headers move into a rules[] entry.pathPrefixes entries become paths entries, each with a value field.coldStartTimeoutFailoverRef splits: the service reference moves to coldStart.fallback, and timeoutSeconds moves to timeouts.readiness as a duration string ("45s").replicas, scaledownPeriod, and initialCooldownPeriod move to the ScaledObject.name, apiVersion, kind) moves to the ScaledObject’s scaleTargetRef.The HTTPScaledObject controller sets an owner reference on the KEDA ScaledObject it creates.
Deleting the HTTPScaledObject triggers Kubernetes garbage collection, which cascade-deletes the owned ScaledObject.
To prevent this, add the httpscaledobject.keda.sh/orphan-scaledobject annotation to the HTTPScaledObject before deleting it.
The controller removes the owner reference from the ScaledObject, so it survives deletion.
Set these environment variables before running the commands:
NAME=my-app
NAMESPACE=default
Annotate the HTTPScaledObject to orphan its ScaledObject:
kubectl annotate httpscaledobject $NAME -n $NAMESPACE \
httpscaledobject.keda.sh/orphan-scaledobject=true
The controller removes the owner reference from the ScaledObject.
Verify that the owner reference was removed:
kubectl get scaledobject $NAME -n $NAMESPACE \
-o jsonpath='{.metadata.ownerReferences}'
The output is empty when the owner reference has been removed.
Create the InterceptorRoute:
kubectl apply -f interceptor-route.yaml
When the InterceptorRoute shares the same namespace and name as the HTTPScaledObject, the routing table automatically gives the InterceptorRoute precedence and skips the HTTPScaledObject.
Update the ScaledObject trigger metadata.
The auto-created ScaledObject references the HTTPScaledObject by name in its trigger metadata.
Replace httpScaledObject with interceptorRoute so the external scaler resolves metrics from the InterceptorRoute:
kubectl patch scaledobject $NAME -n $NAMESPACE --type=json \
-p '[{"op": "remove", "path": "/spec/triggers/0/metadata/httpScaledObject"},
{"op": "add", "path": "/spec/triggers/0/metadata/interceptorRoute", "value": "'$NAME'"}]'
Also compare the remaining fields with the ScaledObject YAML from the conversion example and adjust minReplicaCount, maxReplicaCount, or cooldownPeriod as needed.
Verify the ScaledObject is still present and active:
kubectl get scaledobject $NAME -n $NAMESPACE
Delete the old HTTPScaledObject:
kubectl delete httpscaledobject $NAME -n $NAMESPACE
The ScaledObject remains in the cluster because the owner reference was removed in step 1.
GitOps: In one commit, add the orphan annotation to the
HTTPScaledObjectmanifest, add theInterceptorRoutemanifest, and update theScaledObjecttrigger metadata (httpScaledObject→interceptorRoute). In the next commit, remove theHTTPScaledObjectmanifest.
| HTTPScaledObject field | InterceptorRoute field |
|---|---|
spec.hosts | spec.rules[].hosts |
spec.pathPrefixes | spec.rules[].paths[].value |
spec.headers | spec.rules[].headers |
spec.scaleTargetRef.service | spec.target.service |
spec.scaleTargetRef.port | spec.target.port |
spec.scaleTargetRef.portName | spec.target.portName |
spec.coldStartTimeoutFailoverRef.service | spec.coldStart.fallback.service.name |
spec.coldStartTimeoutFailoverRef.port | spec.coldStart.fallback.service.port |
spec.coldStartTimeoutFailoverRef.portName | spec.coldStart.fallback.service.portName |
spec.coldStartTimeoutFailoverRef.timeoutSeconds | spec.timeouts.readiness (as Duration, e.g. "30s") |
spec.scalingMetric.concurrency.targetValue | spec.scalingMetric.concurrency.targetValue |
spec.scalingMetric.requestRate.targetValue | spec.scalingMetric.requestRate.targetValue |
spec.scalingMetric.requestRate.window | spec.scalingMetric.requestRate.window |
spec.scalingMetric.requestRate.granularity | spec.scalingMetric.requestRate.granularity |
spec.targetPendingRequests | spec.scalingMetric |
spec.timeouts.conditionWait | spec.timeouts.readiness |
spec.timeouts.responseHeader | spec.timeouts.responseHeader |
| (new) | spec.timeouts.request |
| HTTPScaledObject field | ScaledObject field |
|---|---|
spec.scaleTargetRef.name | spec.scaleTargetRef.name |
spec.scaleTargetRef.apiVersion | spec.scaleTargetRef.apiVersion |
spec.scaleTargetRef.kind | spec.scaleTargetRef.kind |
spec.replicas.min | spec.minReplicaCount |
spec.replicas.max | spec.maxReplicaCount |
spec.scaledownPeriod | spec.cooldownPeriod |
spec.initialCooldownPeriod | spec.initialCooldownPeriod |