Docs
Wave Karpenter
CRDs

Wave Karpenter CRDs

These kinds configure Wave Karpenter in CRD Mode. Use WaveKarpenterNodeWarmup for Node Warmup and WaveKarpenterSpotPlacement for Spot Workload Placement. Both kinds require Wave Karpenter to be enabled in your installation.

WaveKarpenterNodeWarmup

WaveKarpenterNodeWarmup configures proactive node provisioning for a Karpenter NodePool. It targets the NodePool by name and combines a warmup mode (warmupType) with a scheduling mode (scheduleType) to select one of three warmup strategies.

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

apiVersion: wavek8s.com/v1alpha1
kind: WaveKarpenterNodeWarmup
metadata:
  name: checkout-warmup
spec:
  nodepoolName: default-nodepool
  warmupType: max_pod      # max_pod | trigger
  scheduleType: scheduled  # always | scheduled
  enabled: true
  enableImagePrepull: true
  options:
    cronExpression: "0 0 9 * * 1-5 *"
    cronDurationMin: 540
    podSizeBufferPct: 20
    consolidationDisabledMin: 30
    useAllAzs: true
    excludeStatefulsetPods: false

Spec fields

FieldTypeRequiredDefaultDescription
nodepoolNamestringYesName of the target Karpenter NodePool. Wave resolves this to the NodePool's UID at reconcile time. A missing NodePool reports RefNotFound and self-heals when the NodePool appears.
warmupTypestringYesWarmup algorithm: max_pod (UI: Max Pod) provisions a node sized for the largest pod in the NodePool; trigger (UI: Trigger) provisions nodes with an explicit vCPU/memory/count spec. Unrecognized values are not rejected — they are silently treated as max_pod, so double-check spelling.
scheduleTypestringYesActivation schedule: always (UI: Always) runs continuously; scheduled (UI: Scheduled) activates on a cron expression. Unrecognized values are not rejected — they are silently treated as always, so double-check spelling.
enabledbooleanNotrueWhether this warmup rule is active. Set to false to pause without deleting the CR.
enableImagePrepullbooleanNofalsePre-pull container images onto warmed nodes to eliminate image-pull latency on pod startup.
optionsobjectSee NotesMode-specific configuration. Required fields differ by (warmupType, scheduleType) combination — see the per-combination tables below.

options fields — max_pod + always

FieldTypeRequiredDefaultDescription
podSizeBufferPctintegerNo10Safety margin (%) added above the observed max-pod size when determining the node to provision.
consolidationDisabledMinintegerNo10Minutes Karpenter consolidation stays paused on the NodePool after each provisioning event.
useAllAzsbooleanNofalseSpread warmed nodes across all availability zones.
excludeStatefulsetPodsbooleanNotrueExclude StatefulSet pods from the max-pod-size calculation.

options fields — max_pod + scheduled

FieldTypeRequiredDefaultDescription
cronExpressionstringYes7-field cron expression (sec min hour dom month dow year) controlling when warmup activates. Example: "0 0 9 * * 1-5 *" (weekdays at 09:00).
cronDurationMinintegerYesDuration (minutes) the warmed node stays provisioned per cron firing before Karpenter may reclaim it.
podSizeBufferPctintegerNo10Safety margin (%) above the observed max-pod size.
consolidationDisabledMinintegerNo10Minutes consolidation stays paused after the window closes.
useAllAzsbooleanNofalseSpread warmed nodes across all availability zones.
excludeStatefulsetPodsbooleanNotrueExclude StatefulSet pods from the max-pod-size calculation.

options fields — trigger + scheduled

FieldTypeRequiredDefaultDescription
cronExpressionstringYes7-field cron expression (sec min hour dom month dow year) controlling when warmup triggers.
nodeVcpusnumberYesvCPU count for the provisioned node. Must be a supported value (e.g. 8.0).
nodeMemoryGinumberYesMemory (GiB) for the provisioned node. Must be a supported value (e.g. 32.0).
nodeTargetCountintegerYesNumber of warm nodes to provision.
consolidationDisabledMinintegerNo10Minutes consolidation stays paused after the trigger fires.

Notes

  • Valid (warmupType, scheduleType) combinations. Three combinations are accepted; one is explicitly rejected:

    warmupTypescheduleTypeResult
    max_podalwaysContinuously monitors and provisions when no existing node fits the largest pod
    max_podscheduledSame as above, but only during the cron window
    triggerscheduledProvisions a fixed node spec on the cron schedule
    triggeralwaysRejected — reports ValidationFailed
  • options is conditionally required. max_pod + always can omit the options block entirely (all fields have defaults). All other combinations require at least the fields marked Required in the tables above; omitting them reports ValidationFailed.

  • Cron format is 7-field. Cron expressions follow the sec min hour dom month dow year format. Example: "0 0 9 * * 1-5 *" fires at 09:00:00 on weekdays every year.

  • nodeVcpus and nodeMemoryGi must be supported values. The trigger strategy only provisions node types Wave explicitly supports. Unsupported values report ValidationFailed.

  • Overlapping cron windows on the same NodePool are rejected. Two max_pod + scheduled warmups targeting the same NodePool whose cron windows overlap report Conflicted. Resolve by adjusting the cron expression or duration. This check does not apply across NodePools or to trigger warmups.

  • max_pod + always and max_pod + scheduled cannot coexist on the same NodePool. Only one schedule type (Always or Scheduled) is allowed per NodePool for max_pod warmup. Attempting to add a second with a different scheduleType reports Conflicted.

  • RefNotFound self-heals. A CR whose nodepoolName does not match any live Karpenter NodePool succeeds at kubectl apply time. Wave reports RefNotFound in .status.conditions and reconciles automatically when the NodePool appears.


WaveKarpenterSpotPlacement

WaveKarpenterSpotPlacement configures automatic On-Demand / Spot replica splitting for a single workload. Wave injects nodeAffinity rules via a MutatingWebhookConfiguration — no changes to your workload YAML are required.

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

apiVersion: wavek8s.com/v1alpha1
kind: WaveKarpenterSpotPlacement
metadata:
  name: checkout-spot-placement
  namespace: payment
spec:
  targetRef:
    kind: Deployment
    name: checkout-api
  podSplitThreshold: 5
  placementStrategy: prefer
  enabled: true

Spec fields

FieldTypeRequiredDefaultDescription
targetRefobjectYesWorkload to manage spot placement for.
targetRef.kindstringYesDeployment, Rollout (Argo Rollouts), or DeploymentConfig (OpenShift).
targetRef.namestringYesName of the workload in the same namespace as the CR.
podSplitThresholdintegerYesKnown in the UI as Pod Spot Threshold. Pods with ordinal index ≤ threshold run On-Demand; index > threshold run on Spot (threshold 5 → pods 1–5 On-Demand, 6+ Spot). Must be >= 1.
placementStrategystringYesprefer — prefer Spot, fall back to On-Demand if unavailable. require — force Spot only; pods remain Pending if no Spot capacity exists. Unknown values report ValidationFailed from Wave at reconcile time.
enabledbooleanNotrueWhether the webhook injects affinity rules for this workload. Set to false to disable without deleting the CR.
⚠️

Only one WaveKarpenterSpotPlacement per workload. Two CRs targeting the same workload (same namespace, name, and kind) under different CR names report ValidationFailed in .status.conditions. The first CR reconciled retains the active configuration; the second is rejected until the conflict is resolved.

Notes

  • Namespaced — create the CR in the same namespace as the target workload. The CR and the workload must share the namespace; cross-namespace targeting is not supported.

  • targetRef.kind should be one of Deployment, Rollout, or DeploymentConfig. Unrecognized values are not rejected — they are silently treated as Deployment, so double-check spelling.

  • podSplitThreshold must be >= 1. A value of 0 reports ValidationFailed. The CRD schema does not enforce a minimum at the API server level, so this check runs at reconcile time.

  • TargetNotFound self-heals. A CR whose target workload does not yet exist succeeds at kubectl apply time. Wave reports TargetNotFound in .status.conditions and reconciles automatically when the workload appears. Apply order does not matter.

  • Webhook TLS is required. Spot Placement depends on a registered MutatingWebhookConfiguration with a valid TLS certificate. Without it, affinity rules are never injected. See Getting Started for TLS setup.

  • prefer vs require. Use prefer for most workloads — pods fall back to On-Demand if Spot capacity is unavailable. Use require only for fault-tolerant batch jobs that should never run On-Demand.