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: falseSpec fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
nodepoolName | string | Yes | — | Name 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. |
warmupType | string | Yes | — | Warmup 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. |
scheduleType | string | Yes | — | Activation 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. |
enabled | boolean | No | true | Whether this warmup rule is active. Set to false to pause without deleting the CR. |
enableImagePrepull | boolean | No | false | Pre-pull container images onto warmed nodes to eliminate image-pull latency on pod startup. |
options | object | See Notes | — | Mode-specific configuration. Required fields differ by (warmupType, scheduleType) combination — see the per-combination tables below. |
options fields — max_pod + always
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
podSizeBufferPct | integer | No | 10 | Safety margin (%) added above the observed max-pod size when determining the node to provision. |
consolidationDisabledMin | integer | No | 10 | Minutes Karpenter consolidation stays paused on the NodePool after each provisioning event. |
useAllAzs | boolean | No | false | Spread warmed nodes across all availability zones. |
excludeStatefulsetPods | boolean | No | true | Exclude StatefulSet pods from the max-pod-size calculation. |
options fields — max_pod + scheduled
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
cronExpression | string | Yes | — | 7-field cron expression (sec min hour dom month dow year) controlling when warmup activates. Example: "0 0 9 * * 1-5 *" (weekdays at 09:00). |
cronDurationMin | integer | Yes | — | Duration (minutes) the warmed node stays provisioned per cron firing before Karpenter may reclaim it. |
podSizeBufferPct | integer | No | 10 | Safety margin (%) above the observed max-pod size. |
consolidationDisabledMin | integer | No | 10 | Minutes consolidation stays paused after the window closes. |
useAllAzs | boolean | No | false | Spread warmed nodes across all availability zones. |
excludeStatefulsetPods | boolean | No | true | Exclude StatefulSet pods from the max-pod-size calculation. |
options fields — trigger + scheduled
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
cronExpression | string | Yes | — | 7-field cron expression (sec min hour dom month dow year) controlling when warmup triggers. |
nodeVcpus | number | Yes | — | vCPU count for the provisioned node. Must be a supported value (e.g. 8.0). |
nodeMemoryGi | number | Yes | — | Memory (GiB) for the provisioned node. Must be a supported value (e.g. 32.0). |
nodeTargetCount | integer | Yes | — | Number of warm nodes to provision. |
consolidationDisabledMin | integer | No | 10 | Minutes 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 -
optionsis conditionally required.max_pod+alwayscan omit theoptionsblock entirely (all fields have defaults). All other combinations require at least the fields marked Required in the tables above; omitting them reportsValidationFailed. -
Cron format is 7-field. Cron expressions follow the
sec min hour dom month dow yearformat. Example:"0 0 9 * * 1-5 *"fires at 09:00:00 on weekdays every year. -
nodeVcpusandnodeMemoryGimust be supported values. The trigger strategy only provisions node types Wave explicitly supports. Unsupported values reportValidationFailed. -
Overlapping cron windows on the same NodePool are rejected. Two
max_pod+scheduledwarmups targeting the same NodePool whose cron windows overlap reportConflicted. Resolve by adjusting the cron expression or duration. This check does not apply across NodePools or totriggerwarmups. -
max_pod+alwaysandmax_pod+scheduledcannot coexist on the same NodePool. Only one schedule type (Always or Scheduled) is allowed per NodePool formax_podwarmup. Attempting to add a second with a differentscheduleTypereportsConflicted. -
RefNotFoundself-heals. A CR whosenodepoolNamedoes not match any live Karpenter NodePool succeeds atkubectl applytime. Wave reportsRefNotFoundin.status.conditionsand 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: trueSpec fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
targetRef | object | Yes | — | Workload to manage spot placement for. |
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. |
podSplitThreshold | integer | Yes | — | Known 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. |
placementStrategy | string | Yes | — | prefer — 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. |
enabled | boolean | No | true | Whether 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.kindshould be one ofDeployment,Rollout, orDeploymentConfig. Unrecognized values are not rejected — they are silently treated asDeployment, so double-check spelling. -
podSplitThresholdmust be>= 1. A value of0reportsValidationFailed. The CRD schema does not enforce a minimum at the API server level, so this check runs at reconcile time. -
TargetNotFoundself-heals. A CR whose target workload does not yet exist succeeds atkubectl applytime. Wave reportsTargetNotFoundin.status.conditionsand 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.
-
prefervsrequire. Usepreferfor most workloads — pods fall back to On-Demand if Spot capacity is unavailable. Userequireonly for fault-tolerant batch jobs that should never run On-Demand.