diff --git a/api/hypershift/v1beta1/aws.go b/api/hypershift/v1beta1/aws.go
index aea0741ef0bd..013c2f95937c 100644
--- a/api/hypershift/v1beta1/aws.go
+++ b/api/hypershift/v1beta1/aws.go
@@ -59,8 +59,21 @@ type AWSNodePoolPlatform struct {
Placement *PlacementOptions `json:"placement,omitempty"`
}
+// AWSSpotMarketOptions defines configuration for AWS Spot instances
+type AWSSpotMarketOptions struct {
+ // maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance.
+ // If omitted, the On‑Demand price is used as the ceiling.
+ // Example: "0.0739"
+ // Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is "0"; scientific notation is not allowed.
+ // +optional
+ // +kubebuilder:validation:Pattern=`^(0|[1-9][0-9]{0,9})(\.[0-9]{1,6})?$`
+ // +kubebuilder:validation:MaxLength=17
+ MaxPrice *string `json:"maxPrice,omitempty"`
+}
+
// PlacementOptions specifies the placement options for the EC2 instances.
// +kubebuilder:validation:XValidation:rule="has(self.tenancy) && self.tenancy == 'host' ? !has(self.capacityReservation) : true", message="AWS Capacity Reservations cannot be used with Dedicated Hosts (tenancy 'host')"
+// +kubebuilder:validation:XValidation:rule="has(self.spotMarketOptions) ? (!has(self.capacityReservation) && (!has(self.tenancy) || self.tenancy == 'default')) : true", message="spotMarketOptions is incompatible with capacityReservation and requires tenancy to be 'default' or unset (not 'dedicated' or 'host')"
type PlacementOptions struct {
// tenancy indicates if instance should run on shared or single-tenant hardware.
//
@@ -83,6 +96,12 @@ type PlacementOptions struct {
//
// +optional
CapacityReservation *CapacityReservationOptions `json:"capacityReservation,omitempty"`
+
+ // spotMarketOptions specifies options for using AWS Spot instances.
+ // When specified, instances will be launched as Spot instances with the given configuration.
+ // Mutually exclusive with capacityReservation, and tenancy must be unset or set to "default".
+ // +optional
+ SpotMarketOptions *AWSSpotMarketOptions `json:"spotMarketOptions,omitempty"`
}
// MarketType describes the market type of the CapacityReservation for an Instance.
diff --git a/api/hypershift/v1beta1/zz_generated.deepcopy.go b/api/hypershift/v1beta1/zz_generated.deepcopy.go
index 41a13b0ab84e..a09baf42c065 100644
--- a/api/hypershift/v1beta1/zz_generated.deepcopy.go
+++ b/api/hypershift/v1beta1/zz_generated.deepcopy.go
@@ -489,6 +489,26 @@ func (in *AWSSharedVPCRolesRef) DeepCopy() *AWSSharedVPCRolesRef {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AWSSpotMarketOptions) DeepCopyInto(out *AWSSpotMarketOptions) {
+ *out = *in
+ if in.MaxPrice != nil {
+ in, out := &in.MaxPrice, &out.MaxPrice
+ *out = new(string)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AWSSpotMarketOptions.
+func (in *AWSSpotMarketOptions) DeepCopy() *AWSSpotMarketOptions {
+ if in == nil {
+ return nil
+ }
+ out := new(AWSSpotMarketOptions)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AddressPair) DeepCopyInto(out *AddressPair) {
*out = *in
@@ -3114,6 +3134,11 @@ func (in *PlacementOptions) DeepCopyInto(out *PlacementOptions) {
*out = new(CapacityReservationOptions)
(*in).DeepCopyInto(*out)
}
+ if in.SpotMarketOptions != nil {
+ in, out := &in.SpotMarketOptions, &out.SpotMarketOptions
+ *out = new(AWSSpotMarketOptions)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementOptions.
diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml
index 16ed60c7d50d..40d6aec71081 100644
--- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml
+++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/AAA_ungated.yaml
@@ -484,6 +484,22 @@ spec:
requires a Capacity Reservation ID
rule: 'has(self.marketType) && self.marketType == ''CapacityBlocks''
? has(self.id) : true'
+ spotMarketOptions:
+ description: |-
+ spotMarketOptions specifies options for using AWS Spot instances.
+ When specified, instances will be launched as Spot instances with the given configuration.
+ Mutually exclusive with capacityReservation, and tenancy must be unset or set to "default".
+ properties:
+ maxPrice:
+ description: |-
+ maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance.
+ If omitted, the On‑Demand price is used as the ceiling.
+ Example: "0.0739"
+ Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is "0"; scientific notation is not allowed.
+ maxLength: 17
+ pattern: ^(0|[1-9][0-9]{0,9})(\.[0-9]{1,6})?$
+ type: string
+ type: object
tenancy:
description: |-
tenancy indicates if instance should run on shared or single-tenant hardware.
@@ -506,6 +522,12 @@ spec:
Hosts (tenancy 'host')
rule: 'has(self.tenancy) && self.tenancy == ''host'' ? !has(self.capacityReservation)
: true'
+ - message: spotMarketOptions is incompatible with capacityReservation
+ and requires tenancy to be 'default' or unset (not 'dedicated'
+ or 'host')
+ rule: 'has(self.spotMarketOptions) ? (!has(self.capacityReservation)
+ && (!has(self.tenancy) || self.tenancy == ''default''))
+ : true'
resourceTags:
description: |-
resourceTags is an optional list of additional tags to apply to AWS node
diff --git a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml
index 5b02e316ca55..2009add3f369 100644
--- a/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml
+++ b/api/hypershift/v1beta1/zz_generated.featuregated-crd-manifests/nodepools.hypershift.openshift.io/OpenStack.yaml
@@ -484,6 +484,22 @@ spec:
requires a Capacity Reservation ID
rule: 'has(self.marketType) && self.marketType == ''CapacityBlocks''
? has(self.id) : true'
+ spotMarketOptions:
+ description: |-
+ spotMarketOptions specifies options for using AWS Spot instances.
+ When specified, instances will be launched as Spot instances with the given configuration.
+ Mutually exclusive with capacityReservation, and tenancy must be unset or set to "default".
+ properties:
+ maxPrice:
+ description: |-
+ maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance.
+ If omitted, the On‑Demand price is used as the ceiling.
+ Example: "0.0739"
+ Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is "0"; scientific notation is not allowed.
+ maxLength: 17
+ pattern: ^(0|[1-9][0-9]{0,9})(\.[0-9]{1,6})?$
+ type: string
+ type: object
tenancy:
description: |-
tenancy indicates if instance should run on shared or single-tenant hardware.
@@ -506,6 +522,12 @@ spec:
Hosts (tenancy 'host')
rule: 'has(self.tenancy) && self.tenancy == ''host'' ? !has(self.capacityReservation)
: true'
+ - message: spotMarketOptions is incompatible with capacityReservation
+ and requires tenancy to be 'default' or unset (not 'dedicated'
+ or 'host')
+ rule: 'has(self.spotMarketOptions) ? (!has(self.capacityReservation)
+ && (!has(self.tenancy) || self.tenancy == ''default''))
+ : true'
resourceTags:
description: |-
resourceTags is an optional list of additional tags to apply to AWS node
diff --git a/client/applyconfiguration/hypershift/v1beta1/awsspotmarketoptions.go b/client/applyconfiguration/hypershift/v1beta1/awsspotmarketoptions.go
new file mode 100644
index 000000000000..ada7165a350c
--- /dev/null
+++ b/client/applyconfiguration/hypershift/v1beta1/awsspotmarketoptions.go
@@ -0,0 +1,38 @@
+/*
+
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1beta1
+
+// AWSSpotMarketOptionsApplyConfiguration represents a declarative configuration of the AWSSpotMarketOptions type for use
+// with apply.
+type AWSSpotMarketOptionsApplyConfiguration struct {
+ MaxPrice *string `json:"maxPrice,omitempty"`
+}
+
+// AWSSpotMarketOptionsApplyConfiguration constructs a declarative configuration of the AWSSpotMarketOptions type for use with
+// apply.
+func AWSSpotMarketOptions() *AWSSpotMarketOptionsApplyConfiguration {
+ return &AWSSpotMarketOptionsApplyConfiguration{}
+}
+
+// WithMaxPrice sets the MaxPrice field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the MaxPrice field is set to the value of the last call.
+func (b *AWSSpotMarketOptionsApplyConfiguration) WithMaxPrice(value string) *AWSSpotMarketOptionsApplyConfiguration {
+ b.MaxPrice = &value
+ return b
+}
diff --git a/client/applyconfiguration/hypershift/v1beta1/placementoptions.go b/client/applyconfiguration/hypershift/v1beta1/placementoptions.go
index 43d5cc98dc87..aab1defde070 100644
--- a/client/applyconfiguration/hypershift/v1beta1/placementoptions.go
+++ b/client/applyconfiguration/hypershift/v1beta1/placementoptions.go
@@ -22,6 +22,7 @@ package v1beta1
type PlacementOptionsApplyConfiguration struct {
Tenancy *string `json:"tenancy,omitempty"`
CapacityReservation *CapacityReservationOptionsApplyConfiguration `json:"capacityReservation,omitempty"`
+ SpotMarketOptions *AWSSpotMarketOptionsApplyConfiguration `json:"spotMarketOptions,omitempty"`
}
// PlacementOptionsApplyConfiguration constructs a declarative configuration of the PlacementOptions type for use with
@@ -45,3 +46,11 @@ func (b *PlacementOptionsApplyConfiguration) WithCapacityReservation(value *Capa
b.CapacityReservation = value
return b
}
+
+// WithSpotMarketOptions sets the SpotMarketOptions field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the SpotMarketOptions field is set to the value of the last call.
+func (b *PlacementOptionsApplyConfiguration) WithSpotMarketOptions(value *AWSSpotMarketOptionsApplyConfiguration) *PlacementOptionsApplyConfiguration {
+ b.SpotMarketOptions = value
+ return b
+}
diff --git a/client/applyconfiguration/utils.go b/client/applyconfiguration/utils.go
index b670f9efab1d..37e4a9cb346f 100644
--- a/client/applyconfiguration/utils.go
+++ b/client/applyconfiguration/utils.go
@@ -89,6 +89,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &hypershiftv1beta1.AWSSharedVPCApplyConfiguration{}
case v1beta1.SchemeGroupVersion.WithKind("AWSSharedVPCRolesRef"):
return &hypershiftv1beta1.AWSSharedVPCRolesRefApplyConfiguration{}
+ case v1beta1.SchemeGroupVersion.WithKind("AWSSpotMarketOptions"):
+ return &hypershiftv1beta1.AWSSpotMarketOptionsApplyConfiguration{}
case v1beta1.SchemeGroupVersion.WithKind("AzureAuthenticationConfiguration"):
return &hypershiftv1beta1.AzureAuthenticationConfigurationApplyConfiguration{}
case v1beta1.SchemeGroupVersion.WithKind("AzureKMSKey"):
diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml
index 0423ac0621cf..018f97da78e2 100644
--- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml
+++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-CustomNoUpgrade.crd.yaml
@@ -487,6 +487,22 @@ spec:
requires a Capacity Reservation ID
rule: 'has(self.marketType) && self.marketType == ''CapacityBlocks''
? has(self.id) : true'
+ spotMarketOptions:
+ description: |-
+ spotMarketOptions specifies options for using AWS Spot instances.
+ When specified, instances will be launched as Spot instances with the given configuration.
+ Mutually exclusive with capacityReservation, and tenancy must be unset or set to "default".
+ properties:
+ maxPrice:
+ description: |-
+ maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance.
+ If omitted, the On‑Demand price is used as the ceiling.
+ Example: "0.0739"
+ Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is "0"; scientific notation is not allowed.
+ maxLength: 17
+ pattern: ^(0|[1-9][0-9]{0,9})(\.[0-9]{1,6})?$
+ type: string
+ type: object
tenancy:
description: |-
tenancy indicates if instance should run on shared or single-tenant hardware.
@@ -509,6 +525,12 @@ spec:
Hosts (tenancy 'host')
rule: 'has(self.tenancy) && self.tenancy == ''host'' ? !has(self.capacityReservation)
: true'
+ - message: spotMarketOptions is incompatible with capacityReservation
+ and requires tenancy to be 'default' or unset (not 'dedicated'
+ or 'host')
+ rule: 'has(self.spotMarketOptions) ? (!has(self.capacityReservation)
+ && (!has(self.tenancy) || self.tenancy == ''default''))
+ : true'
resourceTags:
description: |-
resourceTags is an optional list of additional tags to apply to AWS node
diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml
index 5ab729d41251..f350b77149ff 100644
--- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml
+++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-Default.crd.yaml
@@ -487,6 +487,22 @@ spec:
requires a Capacity Reservation ID
rule: 'has(self.marketType) && self.marketType == ''CapacityBlocks''
? has(self.id) : true'
+ spotMarketOptions:
+ description: |-
+ spotMarketOptions specifies options for using AWS Spot instances.
+ When specified, instances will be launched as Spot instances with the given configuration.
+ Mutually exclusive with capacityReservation, and tenancy must be unset or set to "default".
+ properties:
+ maxPrice:
+ description: |-
+ maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance.
+ If omitted, the On‑Demand price is used as the ceiling.
+ Example: "0.0739"
+ Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is "0"; scientific notation is not allowed.
+ maxLength: 17
+ pattern: ^(0|[1-9][0-9]{0,9})(\.[0-9]{1,6})?$
+ type: string
+ type: object
tenancy:
description: |-
tenancy indicates if instance should run on shared or single-tenant hardware.
@@ -509,6 +525,12 @@ spec:
Hosts (tenancy 'host')
rule: 'has(self.tenancy) && self.tenancy == ''host'' ? !has(self.capacityReservation)
: true'
+ - message: spotMarketOptions is incompatible with capacityReservation
+ and requires tenancy to be 'default' or unset (not 'dedicated'
+ or 'host')
+ rule: 'has(self.spotMarketOptions) ? (!has(self.capacityReservation)
+ && (!has(self.tenancy) || self.tenancy == ''default''))
+ : true'
resourceTags:
description: |-
resourceTags is an optional list of additional tags to apply to AWS node
diff --git a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml
index 74e61ab4dd00..061c64454195 100644
--- a/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml
+++ b/cmd/install/assets/hypershift-operator/zz_generated.crd-manifests/nodepools-TechPreviewNoUpgrade.crd.yaml
@@ -487,6 +487,22 @@ spec:
requires a Capacity Reservation ID
rule: 'has(self.marketType) && self.marketType == ''CapacityBlocks''
? has(self.id) : true'
+ spotMarketOptions:
+ description: |-
+ spotMarketOptions specifies options for using AWS Spot instances.
+ When specified, instances will be launched as Spot instances with the given configuration.
+ Mutually exclusive with capacityReservation, and tenancy must be unset or set to "default".
+ properties:
+ maxPrice:
+ description: |-
+ maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance.
+ If omitted, the On‑Demand price is used as the ceiling.
+ Example: "0.0739"
+ Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is "0"; scientific notation is not allowed.
+ maxLength: 17
+ pattern: ^(0|[1-9][0-9]{0,9})(\.[0-9]{1,6})?$
+ type: string
+ type: object
tenancy:
description: |-
tenancy indicates if instance should run on shared or single-tenant hardware.
@@ -509,6 +525,12 @@ spec:
Hosts (tenancy 'host')
rule: 'has(self.tenancy) && self.tenancy == ''host'' ? !has(self.capacityReservation)
: true'
+ - message: spotMarketOptions is incompatible with capacityReservation
+ and requires tenancy to be 'default' or unset (not 'dedicated'
+ or 'host')
+ rule: 'has(self.spotMarketOptions) ? (!has(self.capacityReservation)
+ && (!has(self.tenancy) || self.tenancy == ''default''))
+ : true'
resourceTags:
description: |-
resourceTags is an optional list of additional tags to apply to AWS node
diff --git a/docs/content/how-to/automated-machine-management/aws-spot-instances.md b/docs/content/how-to/automated-machine-management/aws-spot-instances.md
new file mode 100644
index 000000000000..43b1d1c2b5f8
--- /dev/null
+++ b/docs/content/how-to/automated-machine-management/aws-spot-instances.md
@@ -0,0 +1,221 @@
+---
+title: Manage AWS Spot instances in NodePools
+---
+
+AWS Spot instances allow you to run node pools using spare EC2 capacity at significantly reduced costs compared to On-Demand pricing. HyperShift supports configuring node pools to use AWS Spot instances through the `spotMarketOptions` field in the node pool specification.
+
+This guide demonstrates how to configure and manage NodePools with AWS Spot instances in an existing hosted cluster.
+
+## Prerequisites
+
+- An existing HyperShift hosted cluster on AWS
+- Access to the management cluster where the NodePool resources are created
+- Ensure that the AWS service-linked role for Spot is enabled in the account where the hosted cluster is installed. This is a one-time setup per account.
+ - You can verify if the role already exists using the following command:
+ ```sh
+ aws iam get-role --role-name AWSServiceRoleForEC2Spot
+ ```
+ - If the role does not exist, create it with:
+ ```sh
+ aws iam create-service-linked-role --aws-service-name spot.amazonaws.com
+ ```
+
+## Configure NodePool with Spot instances
+
+### Option 1: Create a new NodePool with Spot instances
+
+Create a new NodePool configured to use AWS Spot instances:
+
+```sh
+cat << EOF | oc apply -f -
+apiVersion: hypershift.openshift.io/v1beta1
+kind: NodePool
+metadata:
+ name: spot-nodepool
+ namespace: clusters
+spec:
+ clusterName:
+(Appears on:
+PlacementOptions)
+
+ AWSSpotMarketOptions defines configuration for AWS Spot instances
| Field | +Description | +
|---|---|
+maxPrice
+
+string
+
+ |
+
+(Optional)
+ maxPrice defines the maximum price (USD per instance‑hour) you are willing to pay for a Spot instance. +If omitted, the On‑Demand price is used as the ceiling. +Example: “0.0739” +Format: up to 10 integer digits and up to 6 fractional digits; no leading zeros unless the value is “0”; scientific notation is not allowed. + |
+
(Appears on: @@ -10436,6 +10469,22 @@ CapacityReservationOptions do not support Capacity Reservations. Compatible with “default” and “dedicated” tenancy.
+spotMarketOptions
+
+
+AWSSpotMarketOptions
+
+
+spotMarketOptions specifies options for using AWS Spot instances. +When specified, instances will be launched as Spot instances with the given configuration. +Mutually exclusive with capacityReservation, and tenancy must be unset or set to “default”.
+