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 msSpec fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
targetRef | object | Yes | — | Workload to manage. |
targetRef.kind | string | Yes | — | Deployment, Rollout (Argo Rollouts), or DeploymentConfig (OpenShift). |
targetRef.name | string | Yes | — | Name of the workload in the same namespace as the CR. |
strategy | string | No | performance | Optimization priority: performance (conservative, maintains headroom) or cost (balanced, minimizes excess). |
minReplicas | integer | Yes | — | Minimum replica count. |
maxReplicas | integer | Yes | — | Maximum replica count. |
load | string (enum) | No | networkIn | Primary load metric: requests, networkIn, or customPromql. |
mode | string (enum) | No | simulation | off — 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. |
applicationType | string (enum) | No | cpuIntensive | Workload resource profile: cpuIntensive or memoryIntensive. Steers metric prioritization and fallback calculations. |
thresholds | object | No | — | Per-metric trigger thresholds. When omitted, Autopilot falls back to strategy defaults. |
thresholds.cpu | number | No | — | CPU utilization threshold (%). |
thresholds.memory | number | No | — | Memory utilization threshold (%). |
thresholds.requests | number | No | — | Requests-per-second threshold (for load: requests). |
thresholds.networkIn | number | No | — | Network-in threshold in bytes (for load: networkIn). |
thresholds.customMetric | number | No | — | Threshold for a custom PromQL metric (for load: customPromql). |
cpuUtilizationBasis | string (enum) | No | request | Which CPU value the utilization threshold is measured against: request or limit. Only meaningful for applicationType: cpuIntensive. Wave 3.4.5+. See Notes. |
fallbackOnly | boolean | No | false | When true, Autopilot skips the ML prediction call and scales purely from the threshold-based fallback path. Wave 3.4.5+. |
fallbackCpuUtilization | number | No | 50 | Target CPU % for the fallback HPA when ML data is unavailable. Values above 100 are accepted — see Notes. |
fallbackMemoryUtilization | number | No | 50 | Target memory % for the fallback path. |
cooldownSeconds | integer | No | — | Pass-through: minimum seconds between consecutive scaling actions. Mutually exclusive with stabilizationWindowSeconds — setting both is rejected at kubectl apply by a CRD CEL rule. |
stabilizationWindowSeconds | integer | No | 60 | Look-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. |
ignoreInitialMetricsSec | integer | No | 10 | Warm-up period (s) after pod start during which metrics are ignored. |
gradualScaleInEnabled | boolean | No | false | Step scale-down gradually instead of dropping straight to the target replica count. |
forecastHorizon | integer | No | 0 | Enable predictive scaling: 0 = off, 1 = on. Larger values are reserved for future use. |
latency | integer | No | — | Target latency budget in milliseconds for latency-aware scaling. |
loadPromql | string | Conditional | — | Deprecated. PromQL expression evaluated as the load signal. Required when load: customPromql; omit otherwise. |
Notes
targetRef.kindmust be one ofDeployment,Rollout(Argo Rollouts), orDeploymentConfig(OpenShift). Other values are accepted by the schema but causeValidationFailedat reconcile time.modedefaults tosimulationwhen omitted — Wave monitors and produces recommendations but does not act on them. Setmode: activeexplicitly to let Autopilot scale your workload. When writing"off"in YAML, quote it — bareoffis parsed as booleanfalseby YAML 1.1 parsers (includingkubectl) and fails the string-enum validation.cooldownSecondsandstabilizationWindowSecondsare mutually exclusive (the "Scaling Adjustment Type" in the console). Setting both is rejected atkubectl applytime 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, setstabilizationWindowSeconds: 0.loadPromqlis deprecated and kept only for backwards compatibility. It is required whenload: customPromql; omit it for all otherloadvalues.- 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 bothcooldownSecondsandstabilizationWindowSecondsare omitted, Wave applies a 60-second stabilization window. cpuUtilizationBasis: limitmeasures 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 torequest; a container with neither a request nor a limit is not scaled at all, unchanged from previous behavior. Switching a workload betweenrequestandlimitinvalidates 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: truemakes Autopilot deterministic: no ML prediction is requested, and every scaling decision comes from the threshold-based fallback calculation. Scaling events are recorded with theFallbackapply type. Use it when you want the fast reactive path without the model, or while a workload is still accumulating training data.fallbackCpuUtilizationis 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 msSpec fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
applicationType | string (enum) | Yes | — | cpuIntensive or memoryIntensive. |
strategy | string | Yes | — | performance or cost. |
minReplicas | integer | Yes | — | Minimum replica count to apply during the scheduled window. |
maxReplicas | integer | Yes | — | Maximum replica count to apply during the scheduled window. |
load | string (enum) | No | networkIn | Demand driver: requests, networkIn, or customPromql. When omitted, Wave defaults to networkIn. |
thresholds | object | No | — | Per-metric trigger thresholds for the preset. |
thresholds.cpu | number | No | — | CPU utilization threshold (%). |
thresholds.memory | number | No | — | Memory utilization threshold (%). |
thresholds.requests | number | No | — | Requests-per-second threshold (for load: requests). |
thresholds.networkIn | number | No | — | Network-in threshold in bytes. |
thresholds.customMetric | number | No | — | Threshold for a custom PromQL metric. |
cpuUtilizationBasis | string (enum) | No | request | Which 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. |
fallbackOnly | boolean | No | false | When true, Autopilot skips the ML prediction call and scales purely from the threshold-based fallback path during the scheduled window. Wave 3.4.5+. |
fallbackCpuUtilization | number | No | 50 | Target CPU % for the fallback HPA when ML data is unavailable. |
fallbackMemoryUtilization | number | No | 50 | Target memory % for the fallback path. |
cooldownSeconds | integer | No | — | Pass-through: minimum seconds between consecutive scaling actions. Mutually exclusive with stabilizationWindowSeconds — setting both is rejected at kubectl apply by a CRD CEL rule. |
stabilizationWindowSeconds | integer | No | 60 | Look-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. |
ignoreInitialMetricsSec | integer | No | 10 | Warm-up period (s) after pod start during which metrics are ignored. |
gradualScaleInEnabled | boolean | No | false | Step scale-down gradually instead of dropping to the target count immediately. |
forecastHorizon | integer | No | 0 | Enable predictive scaling: 0 = off, 1 = on. Larger values are reserved for future use. |
latency | integer | No | — | Target 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, andapplicationTypeare all required. loadPromqlis not available on presets — it is deprecated onWaveAutopilotPolicyand kept there only for backwards compatibility.- Deleting a preset that schedules still reference leaves those schedules reporting
RefNotFoundin.status.conditionsuntil 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: trueSpec fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
timing | object | Yes | — | Timing configuration. The kind field selects the variant (see below). |
timing.kind | string | Yes | — | cron, period, or once. Determines which sibling fields are valid. |
mappings | array | No | [] | List of preset-to-targets bindings. |
mappings[].preset | string | Yes (per item) | — | Name of a WaveAutopilotPreset CR. |
mappings[].targets | array | Yes (per item) | — | Workloads to apply the preset to during the window. |
mappings[].targets[].namespace | string | Yes | — | Namespace of the target workload. |
mappings[].targets[].name | string | Yes | — | Name of the target workload. |
enabled | boolean | No | true | Set to false to disable the schedule without deleting the CR. |
timing.kind: cron fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
cronExpression | string | Yes | — | 7-field cron expression (second minute hour day-of-month month day-of-week year). |
cronDurationMin | integer | Yes | — | How long the window stays active, in minutes. |
cronAdvanceMin | integer | No | — | Activate the preset this many minutes before cronExpression fires. |
timing.kind: period fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
daysOfWeek | string[] | Yes | — | Days the window is active: Mon, Tue, Wed, Thu, Fri, Sat, Sun. |
startTime | string | Yes | — | Window start in HH:mm format (24-hour), interpreted in the block's timezone. |
endTime | string | Yes | — | Window end in HH:mm format (24-hour), interpreted in the block's timezone. |
timezone | string | Yes | — | IANA timezone name (e.g. Asia/Seoul, America/New_York, UTC). Applied to startTime and endTime. |
timing.kind: once fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
startDt | string | Yes | — | Window start in yyyy-MM-dd HH:mm format, interpreted in the block's timezone. |
endDt | string | Yes | — | Window end in yyyy-MM-dd HH:mm format, interpreted in the block's timezone. |
timezone | string | Yes | — | IANA 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 reportsTargetNotFoundand self-heals when the workload appears. - A schedule whose referenced preset doesn't exist yet reports
RefNotFoundand self-heals when the preset (same name) is applied. timinguses an internally-tagged union — thekinddiscriminator sits inline alongside the timing fields (not nested under a separate key). The CRD schema does not validate insidetiming; structural errors (wrong field names, missing required fields) surface asValidationFailedfrom Wave rather than being rejected atkubectl applytime.mappingsdefaults to an empty list. A schedule with no mappings is valid but does nothing — always include at least one mapping entry.enableddefaults totruewhen omitted. Set it tofalseto pause a schedule without removing it.