# KEDA Best Practices

Configure the ScaledObject that owns the workload. KEDA manages activation and return to zero; HPA or [KPA](https://docs.kedify.io/features/kedify-pod-autoscaler/) manages active replicas. Use [Kubernetes Autoscaling Best Practices](https://docs.kedify.io/best-practices/kubernetes-autoscaling-best-practices/) for requests, probes and shutdown.

## Set Activation Thresholds Deliberately

The activation threshold controls whether a workload wakes from zero; the scaling target controls capacity once it is running. Activation requires a value **greater than** the threshold: `activationQueueLength: "5"` leaves five queued messages waiting while the workload is at zero. Keep the default activation threshold of zero unless batching is intentional. Use the selected scaler’s [activation parameter](https://keda.sh/docs/latest/concepts/scaling-deployments/#activating-and-scaling-thresholds).

## Choose the Metric Type

For external metrics, use `AverageValue` for total work divided among replicas. `Value` compares the metric directly with its target and multiplies the ratio by the current replica count.

With three replicas, a metric value of 20 and a target of 5, the HPA recommendations before limits and stabilization are:

| `metricType` | Calculation | Replicas |
| --- | --- | --- |
| `AverageValue` | `ceil(20 / 5)` | 4 |
| `Value` | `ceil(3 × 20 / 5)` | 12 |

Set `metricType` on the trigger and check the metric’s units and aggregation. See the [trigger reference](https://keda.sh/docs/latest/reference/scaledobject-spec/#triggers).

## Treat Missing Metrics as a Failure

A [Prometheus scaler](https://keda.sh/docs/latest/scalers/prometheus/) query must return one scalar or vector element. When an empty result means telemetry is missing, set `ignoreNullValues: "false"` under trigger `metadata` so it produces an error and can engage configured fallback. The default `"true"` accepts empty results. Avoid query expressions that replace missing data with zero unless absence really means no work.

Test both a valid zero value and a missing series; they should have different failure behavior.

## Set Idle and Active Replica Counts

To stop when idle but activate with at least three replicas, add this fragment under `ScaledObject.spec`:

```yaml
idleReplicaCount: 0
minReplicaCount: 3
maxReplicaCount: 10
```

Native HPA supports only zero for `idleReplicaCount`. For a permanent warm floor, omit it and set `minReplicaCount` to the required count. Use [initialCooldownPeriod](https://keda.sh/docs/latest/reference/scaledobject-spec/#initialcooldownperiod) for a grace period after ScaledObject creation; it does not restart on every workload activation. See [idle replica behavior](https://keda.sh/docs/latest/reference/scaledobject-spec/#idlereplicacount).

## Scale Workloads to Zero on a Schedule

Workloads with predictable operating hours can consume resources while nobody needs them. Scaling them to zero outside those hours reduces idle resource usage while bringing them back automatically for the next running window.

Define when the workload should run with a Cron trigger and set `minReplicaCount: 0` for off-hours scale-to-zero. Set `desiredReplicas` to a positive count for the running window, not `"0"` for a shutdown window.

This example runs a Deployment during weekdays from 08:00 to 18:00 in Prague:

```yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: app-working-hours
  namespace: default
spec:
  scaleTargetRef:
    name: app
  minReplicaCount: 0
  maxReplicaCount: 2
  cooldownPeriod: 300
  triggers:
    - type: cron
      metadata:
        timezone: Europe/Prague
        start: "0 8 * * 1-5"
        end: "0 18 * * 1-5"
        desiredReplicas: "2"
```

With only this trigger, the Deployment runs at two replicas during the window and returns to zero after the 300-second cooldown. Apply a ScaledObject to each workload; one schedule does not shut down a whole namespace.

Other external triggers can keep workloads running or reactivate them off-hours: Cron sets a replica floor, not a shutdown override. For advanced scheduling, use Kedify [ScalingPolicy](https://docs.kedify.io/features/scaling-policy/) to manage multiple targets, scheduled replica bounds and action priorities. To keep ScaledObject workloads at zero regardless of demand, configure the shutdown action’s [pauseConfig](https://docs.kedify.io/reference/kubernetes-api/scalingpolicy/#actions) with `paused: true` and `replicas: 0`.

Test shutdown and restart transitions before rollout. See the [KEDA Cron scaler guidance](https://keda.sh/docs/latest/scalers/cron/#scale-to-0-during-off-hours) for details.

## Specify ScaledObject Fallback

Fallback defines a replica target after repeated metric failures. The [fallback reference](https://keda.sh/docs/latest/reference/scaledobject-spec/#fallback) supports `Value` and `AverageValue`, excluding CPU/memory triggers and ScaledJobs. Check your installed KEDA version before applying it.

Add this fragment under `ScaledObject.spec`; keep your existing target and triggers:

```yaml
fallback:
  failureThreshold: 3
  replicas: 6
  behavior: currentReplicasIfHigher
```

`currentReplicasIfHigher` uses the larger of the current count and six, avoiding a lower fallback target during an outage. Use `static` for a fixed fallback target. Size it from measured capacity, then test metric failure and recovery both from zero and while running. Fallback does not guarantee pod readiness. See [fallback behavior](https://keda.sh/docs/latest/reference/scaledobject-spec/#fallbackbehavior).

## Specify Horizontal Autoscaler Behavior

This `ScaledObject.spec` fragment permits immediate scale-up and smooths scale-down over five minutes:

```yaml
advanced:
  horizontalPodAutoscalerConfig:
    behavior:
      scaleUp:
        stabilizationWindowSeconds: 0
        policies:
          - type: Percent
            value: 100
            periodSeconds: 60
      scaleDown:
        stabilizationWindowSeconds: 300
        policies:
          - type: Percent
            value: 50
            periodSeconds: 60
```

These values are an example, not capacity guarantees. A scale-up stabilization window delays reactions to short spikes; use one only when the latency budget permits it. Longer scale-down windows retain more idle capacity. Test a demand spike and a return to idle, and inspect the generated HPA/KPA behavior before changing the window again.

## Understand Polling and Autoscaler Evaluation Intervals

| Setting | Controls | Tradeoff |
| --- | --- | --- |
| `pollingInterval` | KEDA trigger checks, including activation | Faster polling increases source/API traffic |
| `cooldownPeriod` | KEDA’s return to zero after inactivity | Longer cooldown keeps warm capacity longer |
| Trigger `useCachedMetrics` | Reuse of the metric refreshed on KEDA’s polling cycle | Faster autoscaler evaluation can still read the same cached value |
| HPA sync period | Native HPA’s active replica evaluation | Cluster-wide control-plane setting, commonly 15 seconds |
| `autoscaling.kedify.io/sync-period-seconds` | Per-workload KPA evaluation | Lower intervals increase controller and metric load |

Changing `pollingInterval` does not change HPA’s sync period. Configure [KPA cadence](https://docs.kedify.io/features/kedify-pod-autoscaler/#configure-the-kpa-evaluation-interval) separately. Use [performance measurements](https://docs.kedify.io/best-practices/autoscaler-performance-and-tuning/) before increasing controller request rates.

### Cache Expensive Metric Queries

For supported triggers, enable [caching](https://keda.sh/docs/latest/concepts/scaling-deployments/#caching-metrics) at the same level as `metadata`:

```yaml
useCachedMetrics: true
```

The cache refreshes on `pollingInterval`; autoscaler evaluations reuse values between refreshes. This reduces source queries but delays visibility of changes. Choose an interval that fits the reaction-time budget. CPU, memory and Cron triggers do not support this cache.

## Scaling Modifiers

[Scaling modifiers](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaling-modifiers) combine named triggers into one metric. The following fragment assumes existing triggers named `queue1` and `queue2` with compatible units and equal per-item processing cost:

```yaml
advanced:
  scalingModifiers:
    formula: "queue1 + queue2"
    target: "5"
    activationTarget: "0"
    metricType: AverageValue
```

Here, one replica is requested per five combined queued items. An average would halve that demand. Check metric units, target semantics and failure behavior before combining triggers. Test each source independently, simultaneous demand and a failed source. For source-specific configuration and authentication, see [built-in KEDA scalers](https://docs.kedify.io/scalers/keda-scalers/).

For releases supporting [per-trigger fallback](https://keda.sh/docs/latest/concepts/scaling-deployments/#scaling-modifiers), `fallback.behavior: scalingModifiers` passes failed triggers as `nil`. A formula such as `primary ?? backup` uses the first healthy metric; if both fail, `fallback.replicas` applies. Without this behavior, trigger fallback bypasses the formula.

## Preserve Running Jobs During Updates

For long-running tasks, consider [ScaledJobs](https://keda.sh/docs/latest/concepts/scaling-jobs/). The default rollout deletes existing Jobs when the ScaledJob changes. To let running Jobs finish and apply the new configuration only to new Jobs, add this under `ScaledJob.spec`:

```yaml
rollout:
  strategy: gradual
```

Choose the [scaling strategy](https://keda.sh/docs/latest/reference/scaledjob-spec/#scalingstrategy) to match queue accounting: `accurate` is intended for metrics that exclude locked messages. Test an update with a Job still running and verify completion and retry behavior. See [rollout semantics](https://keda.sh/docs/latest/reference/scaledjob-spec/#rollout).

## Scope Authentication

Use `TriggerAuthentication` in the ScaledObject’s namespace. Use `ClusterTriggerAuthentication` only when credentials need to be shared across namespaces; its Secret references default to the KEDA installation namespace. Keep credentials out of trigger metadata, grant only required source permissions, and verify fresh metrics after credential rotation. See [authentication scopes](https://keda.sh/docs/latest/concepts/authentication/#authentication-scopes-namespace-vs-cluster).

## Require Admission Validation When Needed

KEDA webhooks default to `failurePolicy: Ignore`, allowing matching writes when the webhook is unavailable. Use `Fail` when validation must be enforced; webhook downtime will then block those writes. Keep ownership validation enabled and verify webhook availability before changing the policy. See [validation enforcement](https://keda.sh/docs/latest/operate/admission-webhooks/#validation-enforcement).

## Alert on Autoscaling Check Latency

[Autoscaling Checks](https://docs.kedify.io/features/autoscaling-checks/) verify the control-plane path. Set a threshold from the expected timing of the enabled check; for example:

```text
kedify_autoscaling_check_duration_seconds > 300
```

Check the overall result and step labels as well as elapsed time. The compatibility labels `hpa_metric` and `hpa_active` apply to both HPA and KPA. Use [workload diagnostics](https://docs.kedify.io/troubleshooting/workload-scaling/) for application failures.

---
Canonical: https://docs.kedify.io/best-practices/keda-best-practices/
Source: src/content/docs/best-practices/keda-best-practices.md
Documentation index: https://docs.kedify.io/llms.txt
