Docs
Wave Autoscale
CRDs

Wave Autoscale CRDs

These kinds configure Wave Autoscale (Autopilot) in CRD Mode. For the feature concepts they drive, see Core Configuration and Autopilot Scheduler.

WaveAutopilotPolicy

WaveAutopilotPolicy configures Autopilot for a single workload — its replica bounds, scaling strategy, load metric, thresholds, and operational mode. See Core Configuration for explanations of each setting.

Scope: namespaced — must be in the same namespace as the target workload.

apiVersion: wavek8s.com/v1alpha1
kind: WaveAutopilotPolicy
metadata:
  name: checkout-api-policy
  namespace: payment
spec:
  targetRef:
    kind: Deployment
    name: checkout-api
  # active = Autopilot applies scaling decisions; simulation = monitor only (default when omitted)
  mode: active
  strategy: performance          # performance | cost
  applicationType: cpuIntensive  # cpuIntensive | memoryIntensive
  minReplicas: 2
  maxReplicas: 20
  load: requests                 # requests | networkIn | customPromql
  thresholds:
    cpu: 70.0
    memory: 80.0
    requests: 500.0
    networkIn: 1048576.0
  cpuUtilizationBasis: request   # request (default) | limit
  fallbackOnly: false            # true = skip ML, run the threshold path only
  fallbackCpuUtilization: 65.0   # fallback HPA target when ML data is unavailable
  fallbackMemoryUtilization: 75.0
  stabilizationWindowSeconds: 300  # mutually exclusive with cooldownSeconds
  ignoreInitialMetricsSec: 60
  gradualScaleInEnabled: true
  forecastHorizon: 1             # 0 = forecasting off (default)
  latency: 250                   # target latency budget in ms

Spec fields

FieldTypeRequiredDefaultDescription
targetRefobjectYesWorkload to manage.
targetRef.kindstringYesDeployment, Rollout (Argo Rollouts), or DeploymentConfig (OpenShift).
targetRef.namestringYesName of the workload in the same namespace as the CR.
strategystringNoperformanceOptimization priority: performance (conservative, maintains headroom) or cost (balanced, minimizes excess).
minReplicasintegerYesMinimum replica count.
maxReplicasintegerYesMaximum replica count.
loadstring (enum)NonetworkInPrimary load metric: requests, networkIn, or customPromql.
modestring (enum)Nosimulationoff — disabled; simulation — Wave monitors and recommends but does not scale; active — Autopilot applies scaling decisions. Must be quoted when set to "off" in YAML (bare off is a YAML 1.1 boolean). See Notes.
applicationTypestring (enum)NocpuIntensiveWorkload resource profile: cpuIntensive or memoryIntensive. Steers metric prioritization and fallback calculations.
thresholdsobjectNoPer-metric trigger thresholds. When omitted, Autopilot falls back to strategy defaults.
thresholds.cpunumberNoCPU utilization threshold (%).
thresholds.memorynumberNoMemory utilization threshold (%).
thresholds.requestsnumberNoRequests-per-second threshold (for load: requests).
thresholds.networkInnumberNoNetwork-in threshold in bytes (for load: networkIn).
thresholds.customMetricnumberNoThreshold for a custom PromQL metric (for load: customPromql).
cpuUtilizationBasisstring (enum)NorequestWhich CPU value the utilization threshold is measured against: request or limit. Only meaningful for applicationType: cpuIntensive. Wave 3.4.5+. See Notes.
fallbackOnlybooleanNofalseWhen true, Autopilot skips the ML prediction call and scales purely from the threshold-based fallback path. Wave 3.4.5+.
fallbackCpuUtilizationnumberNo50Target CPU % for the fallback HPA when ML data is unavailable. Values above 100 are accepted — see Notes.
fallbackMemoryUtilizationnumberNo50Target memory % for the fallback path.
cooldownSecondsintegerNoPass-through: minimum seconds between consecutive scaling actions. Mutually exclusive with stabilizationWindowSeconds — setting both is rejected at kubectl apply by a CRD CEL rule.
stabilizationWindowSecondsintegerNo60Look-back window (s) a scale-down recommendation must hold before applying. Applied by Wave when both scaling-adjustment fields are omitted; stays unset when cooldownSeconds is set. Mutually exclusive with cooldownSeconds — setting both is rejected at kubectl apply.
ignoreInitialMetricsSecintegerNo10Warm-up period (s) after pod start during which metrics are ignored.
gradualScaleInEnabledbooleanNofalseStep scale-down gradually instead of dropping straight to the target replica count.
forecastHorizonintegerNo0Enable predictive scaling: 0 = off, 1 = on. Larger values are reserved for future use.
latencyintegerNoTarget latency budget in milliseconds for latency-aware scaling.
loadPromqlstringConditionalDeprecated. PromQL expression evaluated as the load signal. Required when load: customPromql; omit otherwise.

Notes

  • targetRef.kind must be one of Deployment, Rollout (Argo Rollouts), or DeploymentConfig (OpenShift). Other values are accepted by the schema but cause ValidationFailed at reconcile time.
  • mode defaults to simulation when omitted — Wave monitors and produces recommendations but does not act on them. Set mode: active explicitly to let Autopilot scale your workload. When writing "off" in YAML, quote it — bare off is parsed as boolean false by YAML 1.1 parsers (including kubectl) and fails the string-enum validation.
  • cooldownSeconds and stabilizationWindowSeconds are mutually exclusive (the "Scaling Adjustment Type" in the console). Setting both is rejected at kubectl apply time by a CRD CEL rule. Omitting both causes Wave to apply a 60-second stabilization window — scaling is not immediate. To match the console "None" option, set stabilizationWindowSeconds: 0.
  • loadPromql is deprecated and kept only for backwards compatibility. It is required when load: customPromql; omit it for all other load values.
  • Omitted optional fields use the same defaults as the console: fallback CPU/memory utilization 50, ignore-initial-metrics 10 s, gradual scale-in off, CPU utilization basis request, fallback-only off. When both cooldownSeconds and stabilizationWindowSeconds are omitted, Wave applies a 60-second stabilization window.
  • cpuUtilizationBasis: limit measures CPU utilization against the container's CPU limit instead of its request. Use it when you deliberately set small requests to pack pods densely and want scaling driven by the limit. A container with no CPU limit silently falls back to request; a container with neither a request nor a limit is not scaled at all, unchanged from previous behavior. Switching a workload between request and limit invalidates its trained model — Wave re-trains under the new basis rather than reusing a model fitted to the other one, so expect a short warm-up.
  • fallbackOnly: true makes Autopilot deterministic: no ML prediction is requested, and every scaling decision comes from the threshold-based fallback calculation. Scaling events are recorded with the Fallback apply type. Use it when you want the fast reactive path without the model, or while a workload is still accumulating training data.
  • fallbackCpuUtilization is no longer capped at 100 (Wave 3.4.5+). Running pods above their request or limit basis before scaling out is a legitimate configuration, so values over 100 are accepted. Non-numeric values are still rejected.

WaveAutopilotPreset

WaveAutopilotPreset defines a reusable Autopilot configuration that WaveAutopilotSchedules reference by name. It carries the same tuning knobs as WaveAutopilotPolicy but applies during a scheduled window across any number of workloads. See Autopilot Scheduler for the scheduling model.

Scope: cluster-scoped — no metadata.namespace required.

apiVersion: wavek8s.com/v1alpha1
kind: WaveAutopilotPreset
metadata:
  name: business-hours-perf
spec:
  applicationType: cpuIntensive
  strategy: performance
  minReplicas: 2
  maxReplicas: 20
  load: requests
  thresholds:
    cpu: 70.0
    memory: 80.0
    requests: 500.0
  cpuUtilizationBasis: request   # request (default) | limit
  fallbackOnly: false            # true = skip ML, run the threshold path only
  fallbackCpuUtilization: 65.0
  fallbackMemoryUtilization: 75.0
  stabilizationWindowSeconds: 300  # mutually exclusive with cooldownSeconds
  ignoreInitialMetricsSec: 60
  gradualScaleInEnabled: true
  forecastHorizon: 1             # 0 = forecasting off, 1 = on
  latency: 250                   # target latency budget in ms

Spec fields

FieldTypeRequiredDefaultDescription
applicationTypestring (enum)YescpuIntensive or memoryIntensive.
strategystringYesperformance or cost.
minReplicasintegerYesMinimum replica count to apply during the scheduled window.
maxReplicasintegerYesMaximum replica count to apply during the scheduled window.
loadstring (enum)NonetworkInDemand driver: requests, networkIn, or customPromql. When omitted, Wave defaults to networkIn.
thresholdsobjectNoPer-metric trigger thresholds for the preset.
thresholds.cpunumberNoCPU utilization threshold (%).
thresholds.memorynumberNoMemory utilization threshold (%).
thresholds.requestsnumberNoRequests-per-second threshold (for load: requests).
thresholds.networkInnumberNoNetwork-in threshold in bytes.
thresholds.customMetricnumberNoThreshold for a custom PromQL metric.
cpuUtilizationBasisstring (enum)NorequestWhich CPU value the utilization threshold is measured against: request or limit. Only meaningful for applicationType: cpuIntensive. Wave 3.4.5+. Same semantics as on WaveAutopilotPolicy.
fallbackOnlybooleanNofalseWhen true, Autopilot skips the ML prediction call and scales purely from the threshold-based fallback path during the scheduled window. Wave 3.4.5+.
fallbackCpuUtilizationnumberNo50Target CPU % for the fallback HPA when ML data is unavailable.
fallbackMemoryUtilizationnumberNo50Target memory % for the fallback path.
cooldownSecondsintegerNoPass-through: minimum seconds between consecutive scaling actions. Mutually exclusive with stabilizationWindowSeconds — setting both is rejected at kubectl apply by a CRD CEL rule.
stabilizationWindowSecondsintegerNo60Look-back window (s) a scale-down recommendation must hold before applying. Applied by Wave when both scaling-adjustment fields are omitted; stays unset when cooldownSeconds is set. Mutually exclusive with cooldownSeconds — setting both is rejected at kubectl apply.
ignoreInitialMetricsSecintegerNo10Warm-up period (s) after pod start during which metrics are ignored.
gradualScaleInEnabledbooleanNofalseStep scale-down gradually instead of dropping to the target count immediately.
forecastHorizonintegerNo0Enable predictive scaling: 0 = off, 1 = on. Larger values are reserved for future use.
latencyintegerNoTarget latency budget in milliseconds.

Notes

  • Cluster-scoped — create one preset and reference it from multiple schedules across any namespace.
  • Presets are referenced by name in WaveAutopilotSchedule.spec.mappings[].preset. Many schedules can share one preset.
  • Unlike WaveAutopilotPolicy, where omitted fields fall back to the console defaults, a preset must be fully specified: strategy, minReplicas, maxReplicas, and applicationType are all required.
  • loadPromql is not available on presets — it is deprecated on WaveAutopilotPolicy and kept there only for backwards compatibility.
  • Deleting a preset that schedules still reference leaves those schedules reporting RefNotFound in .status.conditions until the preset reappears (same name) or the schedules are updated.

WaveAutopilotSchedule

WaveAutopilotSchedule applies a WaveAutopilotPreset to one or more workloads during a defined time window. It supports recurring windows (cron or weekly period) and one-shot windows. See Autopilot Scheduler for the full scheduling model.

Scope: cluster-scoped — no metadata.namespace required.

# Cron variant — recurring window on a 7-field cron expression
apiVersion: wavek8s.com/v1alpha1
kind: WaveAutopilotSchedule
metadata:
  name: business-hours-schedule
spec:
  timing:
    kind: cron
    cronExpression: "0 0 9 * * 1-5 *"   # every weekday at 09:00
    cronDurationMin: 540             # window lasts 9 hours (540 minutes)
    cronAdvanceMin: 30               # optional: activate the preset 30 min early
  mappings:
    - preset: business-hours-perf   # name of a WaveAutopilotPreset
      targets:
        - namespace: payment
          name: checkout-api
        - namespace: payment
          name: payment-worker
  enabled: true
---
# Period variant — recurring window on named days of the week
apiVersion: wavek8s.com/v1alpha1
kind: WaveAutopilotSchedule
metadata:
  name: weekday-scale-up
spec:
  timing:
    kind: period
    daysOfWeek:
      - Mon
      - Tue
      - Wed
      - Thu
      - Fri
    startTime: "09:00"
    endTime: "18:00"
    timezone: Asia/Seoul
  mappings:
    - preset: business-hours-perf
      targets:
        - namespace: payment
          name: checkout-api
---
# Once variant — one-shot window for a specific date range
apiVersion: wavek8s.com/v1alpha1
kind: WaveAutopilotSchedule
metadata:
  name: promo-event-scale
spec:
  timing:
    kind: once
    startDt: "2026-11-25 00:00"
    endDt: "2026-11-25 23:59"
    timezone: Asia/Seoul
  mappings:
    - preset: business-hours-perf
      targets:
        - namespace: payment
          name: checkout-api
  enabled: true

Spec fields

FieldTypeRequiredDefaultDescription
timingobjectYesTiming configuration. The kind field selects the variant (see below).
timing.kindstringYescron, period, or once. Determines which sibling fields are valid.
mappingsarrayNo[]List of preset-to-targets bindings.
mappings[].presetstringYes (per item)Name of a WaveAutopilotPreset CR.
mappings[].targetsarrayYes (per item)Workloads to apply the preset to during the window.
mappings[].targets[].namespacestringYesNamespace of the target workload.
mappings[].targets[].namestringYesName of the target workload.
enabledbooleanNotrueSet to false to disable the schedule without deleting the CR.

timing.kind: cron fields

FieldTypeRequiredDefaultDescription
cronExpressionstringYes7-field cron expression (second minute hour day-of-month month day-of-week year).
cronDurationMinintegerYesHow long the window stays active, in minutes.
cronAdvanceMinintegerNoActivate the preset this many minutes before cronExpression fires.

timing.kind: period fields

FieldTypeRequiredDefaultDescription
daysOfWeekstring[]YesDays the window is active: Mon, Tue, Wed, Thu, Fri, Sat, Sun.
startTimestringYesWindow start in HH:mm format (24-hour), interpreted in the block's timezone.
endTimestringYesWindow end in HH:mm format (24-hour), interpreted in the block's timezone.
timezonestringYesIANA timezone name (e.g. Asia/Seoul, America/New_York, UTC). Applied to startTime and endTime.

timing.kind: once fields

FieldTypeRequiredDefaultDescription
startDtstringYesWindow start in yyyy-MM-dd HH:mm format, interpreted in the block's timezone.
endDtstringYesWindow end in yyyy-MM-dd HH:mm format, interpreted in the block's timezone.
timezonestringYesIANA timezone name (e.g. Asia/Seoul, UTC). Applied to startDt and endDt.

Notes

  • Cluster-scoped — a single schedule can target workloads across multiple namespaces via mappings[].targets[].namespace.
  • Workloads are identified by namespace + name. A schedule whose target workload doesn't exist yet reports TargetNotFound and self-heals when the workload appears.
  • A schedule whose referenced preset doesn't exist yet reports RefNotFound and self-heals when the preset (same name) is applied.
  • timing uses an internally-tagged union — the kind discriminator sits inline alongside the timing fields (not nested under a separate key). The CRD schema does not validate inside timing; structural errors (wrong field names, missing required fields) surface as ValidationFailed from Wave rather than being rejected at kubectl apply time.
  • mappings defaults to an empty list. A schedule with no mappings is valid but does nothing — always include at least one mapping entry.
  • enabled defaults to true when omitted. Set it to false to pause a schedule without removing it.