We are happy to release KEDA 2.20.0 🎉
⚠️ Upgrade note: events moved to events.k8s.io (#7781)
With the Kubernetes 0.35 dependency bump, KEDA now records Kubernetes events via the events.k8s.io API group instead of the legacy core events resource. If you deploy KEDA with custom or restricted RBAC, grant the operator create/patch on events.k8s.io/events before upgrading, otherwise event recording will fail. The bundled KEDA manifests and Helm chart already include the updated permissions.
Highlights:
- Introduce new OpenSearch Scaler
- Introduce Elastic Forecast Scaler
- Add
scalingModifiersfallback behavior - Add support for AWS External ID in TriggerAuthentication podIdentity for all AWS scalers
- Add scaler HTTP request metrics for outbound requests made during metric collection
Learn how to deploy KEDA by reading our documentation.
🗓️ The next KEDA release is currently being estimated for 2nd week of September 2026, learn more in our roadmap.
New
- General: Add
scalingModifiersfallback behavior (#7366) - General: Introduce Elastic Forecast Scaler (#7494)
- General: Introduce new OpenSearch Scaler (#7456)
Improvements
- General: Add cooldownPeriod and pollingInterval checks for ScaledObject (#7271)
- General: Add CRD-level validation markers (Minimum, MinLength, MinItems, Enum) for ScaledObject, ScaledJob, ScaleTriggers, and TriggerAuthentication API types (#7533)
- General: Add
--leader-election-idflag to allow configuring the leader election Lease name (#7564) - General: Add scaler HTTP request metrics (
keda_scaler_http_requests_total,keda_scaler_http_request_duration_seconds) for outbound HTTP requests made during scaler metric collection (#6600) - General: Allow more control of TLS versions & ciphers via
KEDA_HTTP_TLS_CIPHER_LIST,KEDA_SERVICE_TLS_CIPHER_LISTandKEDA_SERVICE_MIN_TLS_VERSIONenv vars (#7617) - General: Cap each scalers-cache reader at a per-reader budget derived from
globalHTTPTimeoutsoScalersCache.Closecannot block indefinitely (#7574) - General: Make APIService cert injections optional (#7559)
- General: Remove unconditional
json.MarshalIndentcalls from admission webhook validation hot paths; replace spec-comparisonMarshalIndent-and-string-compare inisRemovingFinalizervariants withreflect.DeepEqual. Prevents webhook OOM under sustained admission load at large scale (observed at ~60k ScaledObjects) (#7670) - AWS Scalers: Add support for AWS External ID in TriggerAuthentication podIdentity for all AWS scalers (SQS, Kinesis, DynamoDB, CloudWatch, etc.) to enable cross-account access scenarios (#6921)
- Elasticsearch Scaler: Add HTTP status check for Elasticsearch errors (#7480)
- Github Runner Scaler: Handle rate limit errors by respecting X-RateLimit-Reset and Retry-After headers and returning cached queue length (#7683)
- Kubernetes Workload Scaler: Add
groupByNodeparameter (#7628) - Metrics API Scaler: Add custom HTTP client timeout (#7549)
- MSSQL Scaler: Add Azure Workload Identity support for Azure SQL authentication (#6104)
- Prometheus Scaler: Emit metric tracking empty responses from Prometheus (#7062)
- RabbitMQ Scaler: Add support for OAuth2 authentication for RabbitMQ over HTTP (#7379)
- Temporal Scaler: Add support for scaling based on Worker Deployment Version backlog via new
workerDeploymentNameandworkerDeploymentBuildIdfields. DeprecatebuildId,selectAllActive, andselectUnversionedbecause those parameters are used for Rules-Based Worker Versioning, which was a short-lived experimental feature that has been deprecated in the Temporal server since December 2024 and will stop being supported soon. Users of Rules-Based Worker Versioning should use Worker Deployments instead. (#7672)
Fixes
- General: Check updated status for Fallback condition instead of ScaledObject (#7488)
- General: Fail fast in
GetMetricswhen the gRPC connection is in Shutdown state instead of waiting for context timeout (#7251) - General: Fix int64 overflow in milli-quantity conversion for very large metric values (#7441)
- General: Fix
keda_scaler_activenot being emitted for CPU and memory triggers (#4945) - General: Fix misleading namespace in error log when secret access is restricted (#7739)
- General: Fix race in scalers cache rebuild that caused transient scaler errors (#7574)
- General: Fix ScaledJob emitting wrong CloudEvent type (
ScaledObjectReadyTypeinstead ofScaledJobReadyType) when transitioning to ready state (#7792) - General: Fix ScaledObject admission webhook to return validation error from
verifyReplicaCount, preventing invalid ScaledObjects from being created (#5954) - General: Fix ScaledObject Ready condition not reflecting HPA status (#7649)
- General: Handle paused scaling directly in reconciler (#7663)
- General: Honor
stderrthresholdwhenlogtostderris enabled by updating klog to v2.140.0 (#7568) - General: Limit projected service account token reads during Vault authentication (#7783)
- General: Reject ScaledObject creation and update when the name exceeds 63 characters (#6998)
- AWS Scalers: Fix TCP connection leak by closing HTTP idle connections on scaler
Close()for SQS, Kinesis, DynamoDB, DynamoDB Streams, and CloudWatch scalers (#7756) - Azure Data Explorer Scaler: Remove clientSecretFromEnv support (#7554)
- Azure Event Hub Scaler: Reject non-positive
unprocessedEventThresholdto prevent integer division by zero when computing lag (#7732) - Azure Pipelines Scaler: Exclude already-assigned jobs from queue length (#7747)
- Cron Scaler: Fix metric name generation so cron expressions with comma-separated values no longer produce invalid metric names (#7448)
- External Scaler: gRPC Pool uses TLS context in the key (#7687)
- Forgejo Scaler: Limit HTTP error response logging (#7469)
- Forgejo Scaler: Return correct activity to enable scale-to-zero (#7527)
- GCP Cloud Tasks Scaler: Implement escapeFilterValue for metric filtering (#7482)
- GCP Scaler: Validate Pub/Sub resource name in BuildMQLQuery (#7468)
- GCP Storage Scaler: Metadata is not printed in the log (#7688)
- Github Runner Scaler: Bound etag and per-repo caches to prevent unbounded memory growth when
enableEtagsis on (#7685) - Github Runner Scaler: Improve URL construction and error handling (#7495)
- Github Runner Scaler: Limit HTTP error response logging (#7469)
- InfluxDB Scaler: Make
authTokenoptional to support unauthenticated InfluxDB instances (#7616) - Loki Scaler: Limit HTTP error response logging (#7469)
- Loki Scaler:
serverAddressnow appends/loki/api/v1/queryto the end of existing path instead of overriding (#7648) - Metrics API Scaler: Fix
aggregateFromKubeServiceEndpointsusing empty label selector that matched all EndpointSlices in the namespace instead of only the target service's (#7641) - Metrics API Scaler: Fix division by zero in average aggregation when all kube service endpoints fail (#7742)
- Metrics API Scaler: Prevent response value reflection in scaler errors (#7693)
- NATS JetStream Scaler: Return an error from
getMaxMsgLagwhen the configured consumer is missing instead of falling back to the stream's last sequence, preventing incorrect scale-up tomaxReplicaCount(#7657) - NATS JetStream Scaler: URL-encode user input in monitoring URL construction (#7483)
- PostgreSQL Scaler: Quote whitespace-containing connection parameters in generated connection strings (#7784)
- PredictKube Scaler: Bump
dysnix/predictkube-libstov0.1.0(drops the predictkube path to the archived/EOLgo-grpc-prometheusand to the deprecatedgolang/protobuf) and use a portable Prometheus-API instant query for the health check so the scaler works against VictoriaMetrics, Thanos and other Prometheus-API-compatible backends (#7745) - Prometheus Scaler: Handle NaN results in the same manner as Inf (#7475)
- Prometheus Scaler: Limit HTTP error response logging (#7469)
- Pulsar Scaler: Drop bearer/basic auth headers on redirects to a different host or on https->http downgrades to prevent credential leakage (#7686)
- RabbitMQ Scaler: Fix AMQP connection leak by recovering channels on the existing connection and closing connections properly (#6266)
- RabbitMQ Scaler: Use SASL EXTERNAL for RabbitMQ AMQP TLS without credentials (#6840)
- Redis Scaler: Use literal command names in Lua script to fix compatibility with Alibaba Cloud Redis Cluster (#7758)
- Solace Scaler: Fix URL escaping for Message VPN and Queue names (#7481)
- Solr Scaler: Use net/url to safely encode query parameters (#7467)
- Splunk Observability Scaler: Add MTS stream handling with context timeout (#7799)
Breaking Changes
- GCP PubSub Scaler: The
subscriptionSizesetting is DEPRECATED and is removed in v2.20 - Usemodeandvalueinstead (#7720) - Huawei Cloudeye Scaler: The
minMetricValuesetting is DEPRECATED and is removed - UseactivationTargetMetricValueinstead (#7436) - IBM MQ Scaler: The
tlssetting code is removed (#6094) - InfluxDB Scaler: The
authTokensetting fromtriggerMetadatais DEPRECATED and is removed in v2.20 - UseauthTokenfromresolvedEnvorauthParamsinstead (#7722)
Other
- General: Migrate event recording RBAC from core
eventstoevents.k8s.io(#7781) - General: Migrate metrics service gRPC response away from Kubernetes API protobuf types for Kubernetes 0.35 (#7781)
- General: Remove dead code from authentication package and drop unused
authModesfield from ArangoDB, Loki, Prometheus and PredictKube scalers (#7726) - General: Use informer cache for ReplicaSet lookups in GetCurrentReplicas to reduce API server load (#7466)
- External Scaler: Fix race condition in
TestWaitForStatecausing flaky test under-racedetector (#7542) - GCP Scaler: Replace
credentialsFromJSONwithcredentialsFromJSONWithType(#7523) - Kafka Scaler: Refactor Kafka Scaler (#7528)
New Contributors
- @matheus30 made their first contribution in #7449
- @keipa made their first contribution in #7461
- @mattshep made their first contribution in #7466
- @maarous made their first contribution in #7477
- @archy-rock3t-cloud made their first contribution in #7514
- @MunemHashmi made their first contribution in #7534
- @Fedosin made their first contribution in #7533
- @Sayrus made their first contribution in #7490
- @TimShilov made their first contribution in #7561
- @RokeshVS made their first contribution in #7547
- @rohansood10 made their first contribution in #7562
- @tcp13equals2 made their first contribution in #7522
- @proudier made their first contribution in #7642
- @pawan-regoti made their first contribution in #7648
- @ManvithaP-hub made their first contribution in #7655
- @alliasgher made their first contribution in #7678
- @robert-blackman made their first contribution in #7668
- @tangobango5 made their first contribution in #6916
- @MO2k4 made their first contribution in #7673
- @mateenali66 made their first contribution in #7692
- @veeral-patel made their first contribution in #7672
- @voron made their first contribution in #7745
- @visualphoenix made their first contribution in #7740
- @KyriosGN0 made their first contribution in #7454
- @cxhello made their first contribution in #7759
- @ggarb made their first contribution in #7670
- @jansworld made their first contribution in #7607
- @pierluigilenoci made their first contribution in #7568
- @Abhicodeitout made their first contribution in #7778
- @musztardem made their first contribution in #7540
- @puneetdixit200 made their first contribution in #7755
Full Changelog: v2.19.0...v2.20.0