Skip to content

ScaledObject always overridden by HPA, unable to scale to 0 #7527

Description

@cloudymax

Report

I am unable to get my ScaledObject to scale my HPA to 0.

  • My deployment has replicas set to 0

  • The scaling metric is correctly read from the source as 0

Metrics:                                              ( current / target ) 
"s0-forgejo" (target average value):  0 / 1
{
  "level": "debug",
  "ts": "2026-03-08T11:54:57Z",
  "logger": "scale_handler",
  "msg": "Getting metrics and activity from scaler",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject",
  "scaler": "trig_one",
  "metricName": "s0-forgejo",
  "metrics": [
    {
      "metricName": "s0-forgejo",
      "metricLabels": null,
      "timestamp": "2026-03-08T11:54:57Z",
      "value": "0"
    }
  ],
  "activity": true,
  "scalerError": null
}
  • my scaledObject minReplicaCount is set to 0

  • The HPA sees the current average value as 0

  currentMetrics:
  - external:
      current:
        averageValue: "0"
      metric:
        name: s0-forgejo
        selector:
          matchLabels:
            scaledobject.keda.sh/name: forgejo-runners-scaledobject
    type: External

Yet the HPA always scales to 1, it is never paused by keda to sacle to 0

status:
  conditions:
  - lastTransitionTime: "2026-03-08T11:47:49Z"
    message: recommended size matches current size
    reason: ReadyForNewScale
    status: "True"
    type: AbleToScale
  - lastTransitionTime: "2026-03-08T11:47:49Z"
    message: 'the HPA was able to successfully calculate a replica count from external
      metric s0-forgejo(&LabelSelector{MatchLabels:map[string]string{scaledobject.keda.sh/name:
      forgejo-runners-scaledobject,},MatchExpressions:[]LabelSelectorRequirement{},})'
    reason: ValidMetricFound
    status: "True"
    type: ScalingActive
  - lastTransitionTime: "2026-03-08T11:47:49Z"
    message: the desired replica count is less than the minimum replica count
    reason: TooFewReplicas
    status: "True"
    type: ScalingLimited

Expected Behavior

I would expect the scaled object tell the HPA to pause scaling to take over the scaling when metrics all show 0

Actual Behavior

HPA always takes precedence over ScaledObject

Steps to Reproduce the Problem

ScaledObject:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  annotations:
    argocd.argoproj.io/tracking-id: runners-app-of-apps:keda.sh/ScaledObject:forgejo/forgejo-runners-scaledobject
    autoscaling.keda.sh/paused: "false"
  finalizers:
  - finalizer.keda.sh
  generation: 6
  labels:
    kubevirt-manager.io/managed: "true"
    scaledobject.keda.sh/name: forgejo-runners-scaledobject
  name: forgejo-runners-scaledobject
  namespace: forgejo
spec:
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          policies:
          - periodSeconds: 30
            type: Pods
            value: 1
          stabilizationWindowSeconds: 60
        scaleUp:
          policies:
          - periodSeconds: 5
            type: Pods
            value: 1
          stabilizationWindowSeconds: 10
  cooldownPeriod: 60
  initialCooldownPeriod: 10
  maxReplicaCount: 10
  minReplicaCount: 0
  pollingInterval: 5
  scaleTargetRef:
    apiVersion: pool.kubevirt.io/v1beta1
    kind: VirtualMachinePool
    name: example-pool
  triggers:
  - authenticationRef:
      name: runner-credentials
    metadata:
      address: https://git.example.org
      global: "false"
      labels: self-hosted
      name: example
      owner: small-hack
    name: trig_one
    type: forgejo-runner
status:
  authenticationsTypes: runner-credentials
  compositeScalerName: composite-metric
  conditions:
  - message: ScaledObject is defined correctly and is ready for scaling
    reason: ScaledObjectReady
    status: "True"
    type: Ready
  - message: Scaling is performed because triggers are active
    reason: ScalerActive
    status: "True"
    type: Active
  - message: No fallbacks are active on this scaled object
    reason: NoFallbackFound
    status: "False"
    type: Fallback
  - status: "False"
    type: Paused
  externalMetricNames:
  - s0-forgejo
  health:
    s0-forgejo:
      numberOfFailures: 0
      status: Happy
  hpaName: keda-hpa-forgejo-runners-scaledobject
  lastActiveTime: "2026-03-08T12:06:42Z"
  originalReplicaCount: 1
  scaleTargetGVKR:
    group: pool.kubevirt.io
    kind: VirtualMachinePool
    resource: virtualmachinepools
    version: v1beta1
  scaleTargetKind: pool.kubevirt.io/v1beta1.VirtualMachinePool
  triggersActivity:
    s0-forgejo:
      isActive: true
  triggersTypes: forgejo-runner

The HPA created by the scaledObject:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  annotations:
    argocd.argoproj.io/tracking-id: runners-app-of-apps:keda.sh/ScaledObject:forgejo/forgejo-runners-scaledobject
    autoscaling.keda.sh/paused: "false"
  labels:
    app.kubernetes.io/managed-by: keda-operator
    app.kubernetes.io/name: keda-hpa-forgejo-runners-scaledobject
    app.kubernetes.io/part-of: forgejo-runners-scaledobject
    app.kubernetes.io/version: 2.19.0
    kubevirt-manager.io/managed: "true"
    scaledobject.keda.sh/name: forgejo-runners-scaledobject
  name: keda-hpa-forgejo-runners-scaledobject
  namespace: forgejo
  ownerReferences:
  - apiVersion: keda.sh/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: ScaledObject
    name: forgejo-runners-scaledobject
    uid: 7ee4c7c4-0e8c-4a7c-a4ac-6adc330c29f1
spec:
  behavior:
    scaleDown:
      policies:
      - periodSeconds: 30
        type: Pods
        value: 1
      selectPolicy: Max
      stabilizationWindowSeconds: 60
    scaleUp:
      policies:
      - periodSeconds: 5
        type: Pods
        value: 1
      selectPolicy: Max
      stabilizationWindowSeconds: 10
  maxReplicas: 10
  metrics:
  - external:
      metric:
        name: s0-forgejo
        selector:
          matchLabels:
            scaledobject.keda.sh/name: forgejo-runners-scaledobject
      target:
        averageValue: "1"
        type: AverageValue
    type: External
  minReplicas: 1
  scaleTargetRef:
    apiVersion: pool.kubevirt.io/v1beta1
    kind: VirtualMachinePool
    name: example-pool
status:
  conditions:
  - lastTransitionTime: "2026-03-08T11:47:49Z"
    message: recommended size matches current size
    reason: ReadyForNewScale
    status: "True"
    type: AbleToScale
  - lastTransitionTime: "2026-03-08T11:47:49Z"
    message: 'the HPA was able to successfully calculate a replica count from external
      metric s0-forgejo(&LabelSelector{MatchLabels:map[string]string{scaledobject.keda.sh/name:
      forgejo-runners-scaledobject,},MatchExpressions:[]LabelSelectorRequirement{},})'
    reason: ValidMetricFound
    status: "True"
    type: ScalingActive
  - lastTransitionTime: "2026-03-08T11:47:49Z"
    message: the desired replica count is less than the minimum replica count
    reason: TooFewReplicas
    status: "True"
    type: ScalingLimited
  currentMetrics:
  - external:
      current:
        averageValue: "0"
      metric:
        name: s0-forgejo
        selector:
          matchLabels:
            scaledobject.keda.sh/name: forgejo-runners-scaledobject
    type: External
  currentReplicas: 1
  desiredReplicas: 1

Logs from KEDA operator

2026-03-08 13:10:27.734	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:27Z",
  "logger": "scale_handler",
  "msg": "Scaler for scaledObject is active",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject",
  "scaler": "trig_one",
  "metricName": "s0-forgejo"
}
	
		2026-03-08 13:10:27.734	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:27Z",
  "logger": "fallback",
  "msg": "Fallback is not enabled, hence skipping the health update to the scaledobject",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject"
}
	
		2026-03-08 13:10:27.734	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:27Z",
  "logger": "scale_handler",
  "msg": "Getting metrics and activity from scaler",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject",
  "scaler": "trig_one",
  "metricName": "s0-forgejo",
  "metrics": [
    {
      "metricName": "s0-forgejo",
      "metricLabels": null,
      "timestamp": "2026-03-08T12:10:27Z",
      "value": "0"
    }
  ],
  "activity": true,
  "scalerError": null
}
	
		2026-03-08 13:10:22.729	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:22Z",
  "logger": "scale_handler",
  "msg": "Scaler for scaledObject is active",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject",
  "scaler": "trig_one",
  "metricName": "s0-forgejo"
}
	
		2026-03-08 13:10:22.729	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:22Z",
  "logger": "fallback",
  "msg": "Fallback is not enabled, hence skipping the health update to the scaledobject",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject"
}
	
		2026-03-08 13:10:22.729	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:22Z",
  "logger": "scale_handler",
  "msg": "Getting metrics and activity from scaler",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject",
  "scaler": "trig_one",
  "metricName": "s0-forgejo",
  "metrics": [
    {
      "metricName": "s0-forgejo",
      "metricLabels": null,
      "timestamp": "2026-03-08T12:10:22Z",
      "value": "0"
    }
  ],
  "activity": true,
  "scalerError": null
}
	
		2026-03-08 13:10:22.247	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:22Z",
  "logger": "grpc_server",
  "msg": "Providing metrics",
  "scaledObjectName": "forgejo-runners-scaledobject",
  "scaledObjectNamespace": "forgejo",
  "metrics": "&ExternalMetricValueList{ListMeta:{   <nil>},Items:[]ExternalMetricValue{ExternalMetricValue{MetricName:s0-forgejo,MetricLabels:map[string]string{},Timestamp:2026-03-08 12:10:22.247593549 +0000 UTC m=+54028.719744875,WindowSeconds:nil,Value:{{0 -3} {<nil>}  DecimalSI},},},}"
}
	
		2026-03-08 13:10:22.247	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:22Z",
  "logger": "fallback",
  "msg": "Fallback is not enabled, hence skipping the health update to the scaledobject",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject"
}
	
		2026-03-08 13:10:22.247	
{
  "level": "debug",
  "ts": "2026-03-08T12:10:22Z",
  "logger": "scale_handler",
  "msg": "Getting metrics from trigger",
  "scaledObject.Namespace": "forgejo",
  "scaledObject.Name": "forgejo-runners-scaledobject",
  "trigger": "trig_one",
  "metricName": "s0-forgejo",
  "metrics": [
    {
      "metricName": "s0-forgejo",
      "metricLabels": null,
      "timestamp": "2026-03-08T12:10:22Z",
      "value": "0"
    }
  ],
  "scalerError": null
}

KEDA Version

2.19.0

Kubernetes Version

1.34

Platform

Other

Scaler Details

Forgejo

Would you be open to contributing a fix?

None

Anything else?

No response

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Ready To Ship

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions