Repository: apecloud/kubeblocks

Issue:
[Features] Stateful Workload should distinguish UPGRADE from other changes
Sometimes when there is an upgrade (image chagne), it is required to RE-CREATE the pod rather than an IN-PLACE Update. 

But currently, KubeBlocks treats all changes to stateful workloads uniformly, without distinguishing between different types of operations (Upgrade v.s. Other Update)
- Upgrade operations (version changes, image updates) --> prefer a RECREATE strategy
- Vertical scaling operations (CPU/memory changes) --> prefers IN-PLACE-UPDATE strategy.



Hints from issue comments:
@github-actions: This issue has been marked as stale because it has been open for 30 days with no activity

Solution patch:
diff --git a/apis/apps/v1/cluster_types.go b/apis/apps/v1/cluster_types.go
index 2aa2b2578..376396bf9 100644
--- a/apis/apps/v1/cluster_types.go
+++ b/apis/apps/v1/cluster_types.go
@@ -451,9 +451,18 @@ type ClusterComponentSpec struct {
 	// Default value is "PreferInPlace"
 	//
 	// +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace}
+	// +kubebuilder:default=PreferInPlace
 	// +optional
 	PodUpdatePolicy *PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"`
 
+	// PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+	//
+	// If not specified, the value of PodUpdatePolicy will be used.
+	//
+	// +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace}
+	// +optional
+	PodUpgradePolicy *PodUpdatePolicyType `json:"podUpgradePolicy,omitempty"`
+
 	// Provides fine-grained control over the spec update process of all instances.
 	//
 	// +optional
diff --git a/apis/apps/v1/component_types.go b/apis/apps/v1/component_types.go
index 6314d08e5..201401bf6 100644
--- a/apis/apps/v1/component_types.go
+++ b/apis/apps/v1/component_types.go
@@ -224,9 +224,19 @@ type ComponentSpec struct {
 	// If that fails, it will fall back to the ReCreate, where pod will be recreated.
 	// Default value is "PreferInPlace"
 	//
+	// +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace}
+	// +kubebuilder:default=PreferInPlace
 	// +optional
 	PodUpdatePolicy *PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"`
 
+	// PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+	//
+	// If not specified, the value of PodUpdatePolicy will be used.
+	//
+	// +kubebuilder:validation:Enum={StrictInPlace,PreferInPlace}
+	// +optional
+	PodUpgradePolicy *PodUpdatePolicyType `json:"podUpgradePolicy,omitempty"`
+
 	// Provides fine-grained control over the spec update process of all instances.
 	//
 	// +optional
diff --git a/apis/apps/v1/componentdefinition_types.go b/apis/apps/v1/componentdefinition_types.go
index 7498345c0..d85d27429 100644
--- a/apis/apps/v1/componentdefinition_types.go
+++ b/apis/apps/v1/componentdefinition_types.go
@@ -469,6 +469,21 @@ type ComponentDefinitionSpec struct {
 	// +optional
 	PodManagementPolicy *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"`
 
+	// Specifies the default update policy for pods when the Component is updated.
+	//
+	// +kubebuilder:validation:Enum={PreferInPlace,ReCreate}
+	// +kubebuilder:default=PreferInPlace
+	// +optional
+	PodUpdatePolicy *PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"`
+
+	// Specifies the default update policy for pods when the Component is upgraded (the service version changes).
+	//
+	// If not specified, the default behavior is the same as `podUpdatePolicy`.
+	//
+	// +kubebuilder:validation:Enum={PreferInPlace,ReCreate}
+	// +optional
+	PodUpgradePolicy *PodUpdatePolicyType `json:"podUpgradePolicy,omitempty"`
+
 	// Defines the namespaced policy rules required by the Component.
 	//
 	// The `policyRules` field is an array of `rbacv1.PolicyRule` objects that define the policy rules
diff --git a/apis/apps/v1/types.go b/apis/apps/v1/types.go
index ff4ff624e..6f8813f65 100644
--- a/apis/apps/v1/types.go
+++ b/apis/apps/v1/types.go
@@ -540,6 +540,9 @@ const (
 	// PreferInPlacePodUpdatePolicyType indicates that we will first attempt an in-place upgrade of the Pod.
 	// If that fails, it will fall back to the ReCreate, where pod will be recreated.
 	PreferInPlacePodUpdatePolicyType PodUpdatePolicyType = "PreferInPlace"
+
+	// ReCreatePodUpdatePolicyType indicates that it will always recreate the Pod.
+	ReCreatePodUpdatePolicyType PodUpdatePolicyType = "ReCreate"
 )
 
 // InstanceUpdateStrategy defines fine-grained control over the spec update process of all instances.
diff --git a/apis/apps/v1/zz_generated.deepcopy.go b/apis/apps/v1/zz_generated.deepcopy.go
index 4995ddf5e..0f1462525 100644
--- a/apis/apps/v1/zz_generated.deepcopy.go
+++ b/apis/apps/v1/zz_generated.deepcopy.go
@@ -371,6 +371,11 @@ func (in *ClusterComponentSpec) DeepCopyInto(out *ClusterComponentSpec) {
 		*out = new(PodUpdatePolicyType)
 		**out = **in
 	}
+	if in.PodUpgradePolicy != nil {
+		in, out := &in.PodUpgradePolicy, &out.PodUpgradePolicy
+		*out = new(PodUpdatePolicyType)
+		**out = **in
+	}
 	if in.InstanceUpdateStrategy != nil {
 		in, out := &in.InstanceUpdateStrategy, &out.InstanceUpdateStrategy
 		*out = new(InstanceUpdateStrategy)
@@ -1213,6 +1218,16 @@ func (in *ComponentDefinitionSpec) DeepCopyInto(out *ComponentDefinitionSpec) {
 		*out = new(appsv1.PodManagementPolicyType)
 		**out = **in
 	}
+	if in.PodUpdatePolicy != nil {
+		in, out := &in.PodUpdatePolicy, &out.PodUpdatePolicy
+		*out = new(PodUpdatePolicyType)
+		**out = **in
+	}
+	if in.PodUpgradePolicy != nil {
+		in, out := &in.PodUpgradePolicy, &out.PodUpgradePolicy
+		*out = new(PodUpdatePolicyType)
+		**out = **in
+	}
 	if in.PolicyRules != nil {
 		in, out := &in.PolicyRules, &out.PolicyRules
 		*out = make([]rbacv1.PolicyRule, len(*in))
@@ -1551,6 +1566,11 @@ func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) {
 		*out = new(PodUpdatePolicyType)
 		**out = **in
 	}
+	if in.PodUpgradePolicy != nil {
+		in, out := &in.PodUpgradePolicy, &out.PodUpgradePolicy
+		*out = new(PodUpdatePolicyType)
+		**out = **in
+	}
 	if in.InstanceUpdateStrategy != nil {
 		in, out := &in.InstanceUpdateStrategy, &out.InstanceUpdateStrategy
 		*out = new(InstanceUpdateStrategy)
diff --git a/apis/workloads/v1/instance_types.go b/apis/workloads/v1/instance_types.go
index bba22e8c7..31ad55066 100644
--- a/apis/workloads/v1/instance_types.go
+++ b/apis/workloads/v1/instance_types.go
@@ -113,6 +113,11 @@ type InstanceSpec struct {
 	// +optional
 	PodUpdatePolicy PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"`
 
+	// PodUpdatePolicy indicates how pods should be upgraded.
+	//
+	// +optional
+	PodUpgradePolicy PodUpdatePolicyType `json:"podUpgradePolicy,omitempty"`
+
 	// A list of roles defined in the system. Instanceset obtains role through pods' role label `kubeblocks.io/role`.
 	//
 	// +optional
diff --git a/apis/workloads/v1/instanceset_types.go b/apis/workloads/v1/instanceset_types.go
index 470eb99c0..451f1a2b1 100644
--- a/apis/workloads/v1/instanceset_types.go
+++ b/apis/workloads/v1/instanceset_types.go
@@ -196,17 +196,16 @@ type InstanceSetSpec struct {
 	// +optional
 	ParallelPodManagementConcurrency *intstr.IntOrString `json:"parallelPodManagementConcurrency,omitempty"`
 
-	// PodUpdatePolicy indicates how pods should be updated
-	//
-	// - `StrictInPlace` indicates that only allows in-place upgrades.
-	// Any attempt to modify other fields will be rejected.
-	// - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod.
-	// If that fails, it will fall back to the ReCreate, where pod will be recreated.
-	// Default value is "PreferInPlace"
+	// PodUpdatePolicy indicates how pods should be updated.
 	//
 	// +optional
 	PodUpdatePolicy PodUpdatePolicyType `json:"podUpdatePolicy,omitempty"`
 
+	// PodUpgradePolicy indicates how pods should be upgraded.
+	//
+	// +optional
+	PodUpgradePolicy PodUpdatePolicyType `json:"podUpgradePolicy,omitempty"`
+
 	// Provides fine-grained control over the spec update process of all instances.
 	//
 	// +optional
diff --git a/config/crd/bases/apps.kubeblocks.io_clusters.yaml b/config/crd/bases/apps.kubeblocks.io_clusters.yaml
index 8a8da3186..1eb67e359 100644
--- a/config/crd/bases/apps.kubeblocks.io_clusters.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_clusters.yaml
@@ -2832,6 +2832,7 @@ spec:
                           type: string
                       type: object
                     podUpdatePolicy:
+                      default: PreferInPlace
                       description: |-
                         PodUpdatePolicy indicates how pods should be updated
 
@@ -2845,6 +2846,16 @@ spec:
                       - StrictInPlace
                       - PreferInPlace
                       type: string
+                    podUpgradePolicy:
+                      description: |-
+                        PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+
+
+                        If not specified, the value of PodUpdatePolicy will be used.
+                      enum:
+                      - StrictInPlace
+                      - PreferInPlace
+                      type: string
                     replicas:
                       default: 1
                       description: Specifies the desired number of replicas in the
@@ -14071,6 +14082,7 @@ spec:
                               type: string
                           type: object
                         podUpdatePolicy:
+                          default: PreferInPlace
                           description: |-
                             PodUpdatePolicy indicates how pods should be updated
 
@@ -14084,6 +14096,16 @@ spec:
                           - StrictInPlace
                           - PreferInPlace
                           type: string
+                        podUpgradePolicy:
+                          description: |-
+                            PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+
+
+                            If not specified, the value of PodUpdatePolicy will be used.
+                          enum:
+                          - StrictInPlace
+                          - PreferInPlace
+                          type: string
                         replicas:
                           default: 1
                           description: Specifies the desired number of replicas in
diff --git a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml
index aba2cf748..c9be67383 100644
--- a/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_componentdefinitions.yaml
@@ -10321,6 +10321,24 @@ spec:
                   - `Parallel`: Creates pods in parallel to match the desired scale without waiting. All pods are deleted at once
                   when scaling down.
                 type: string
+              podUpdatePolicy:
+                default: PreferInPlace
+                description: Specifies the default update policy for pods when the
+                  Component is updated.
+                enum:
+                - PreferInPlace
+                - ReCreate
+                type: string
+              podUpgradePolicy:
+                description: |-
+                  Specifies the default update policy for pods when the Component is upgraded (the service version changes).
+
+
+                  If not specified, the default behavior is the same as `podUpdatePolicy`.
+                enum:
+                - PreferInPlace
+                - ReCreate
+                type: string
               policyRules:
                 description: |-
                   Defines the namespaced policy rules required by the Component.
diff --git a/config/crd/bases/apps.kubeblocks.io_components.yaml b/config/crd/bases/apps.kubeblocks.io_components.yaml
index cfd53b868..7ed168a4b 100644
--- a/config/crd/bases/apps.kubeblocks.io_components.yaml
+++ b/config/crd/bases/apps.kubeblocks.io_components.yaml
@@ -2613,6 +2613,7 @@ spec:
                     type: string
                 type: object
               podUpdatePolicy:
+                default: PreferInPlace
                 description: |-
                   PodUpdatePolicy indicates how pods should be updated
 
@@ -2622,6 +2623,19 @@ spec:
                   - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod.
                   If that fails, it will fall back to the ReCreate, where pod will be recreated.
                   Default value is "PreferInPlace"
+                enum:
+                - StrictInPlace
+                - PreferInPlace
+                type: string
+              podUpgradePolicy:
+                description: |-
+                  PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+
+
+                  If not specified, the value of PodUpdatePolicy will be used.
+                enum:
+                - StrictInPlace
+                - PreferInPlace
                 type: string
               replicas:
                 default: 1
diff --git a/config/crd/bases/workloads.kubeblocks.io_instances.yaml b/config/crd/bases/workloads.kubeblocks.io_instances.yaml
index d4df9b9dc..f22ac00ce 100644
--- a/config/crd/bases/workloads.kubeblocks.io_instances.yaml
+++ b/config/crd/bases/workloads.kubeblocks.io_instances.yaml
@@ -1565,6 +1565,9 @@ spec:
               podUpdatePolicy:
                 description: PodUpdatePolicy indicates how pods should be updated.
                 type: string
+              podUpgradePolicy:
+                description: PodUpdatePolicy indicates how pods should be upgraded.
+                type: string
               roles:
                 description: A list of roles defined in the system. Instanceset obtains
                   role through pods' role label `kubeblocks.io/role`.
diff --git a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml
index 6b4d4660b..16e27bf44 100644
--- a/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml
+++ b/config/crd/bases/workloads.kubeblocks.io_instancesets.yaml
@@ -3094,15 +3094,10 @@ spec:
                   Note: This field will be removed in future version.
                 type: string
               podUpdatePolicy:
-                description: |-
-                  PodUpdatePolicy indicates how pods should be updated
-
-
-                  - `StrictInPlace` indicates that only allows in-place upgrades.
-                  Any attempt to modify other fields will be rejected.
-                  - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod.
-                  If that fails, it will fall back to the ReCreate, where pod will be recreated.
-                  Default value is "PreferInPlace"
+                description: PodUpdatePolicy indicates how pods should be updated.
+                type: string
+              podUpgradePolicy:
+                description: PodUpgradePolicy indicates how pods should be upgraded.
                 type: string
               replicas:
                 default: 1
diff --git a/controllers/apps/cluster/transformer_cluster_component.go b/controllers/apps/cluster/transformer_cluster_component.go
index 7bd349642..8694b38b9 100644
--- a/controllers/apps/cluster/transformer_cluster_component.go
+++ b/controllers/apps/cluster/transformer_cluster_component.go
@@ -274,6 +274,7 @@ func copyAndMergeComponent(oldCompObj, newCompObj *appsv1.Component) *appsv1.Com
 	compObjCopy.Spec.ServiceAccountName = compProto.Spec.ServiceAccountName
 	compObjCopy.Spec.ParallelPodManagementConcurrency = compProto.Spec.ParallelPodManagementConcurrency
 	compObjCopy.Spec.PodUpdatePolicy = compProto.Spec.PodUpdatePolicy
+	compObjCopy.Spec.PodUpgradePolicy = compProto.Spec.PodUpgradePolicy
 	compObjCopy.Spec.InstanceUpdateStrategy = compProto.Spec.InstanceUpdateStrategy
 	compObjCopy.Spec.SchedulingPolicy = compProto.Spec.SchedulingPolicy
 	compObjCopy.Spec.TLSConfig = compProto.Spec.TLSConfig
diff --git a/controllers/apps/component/transformer_component_workload.go b/controllers/apps/component/transformer_component_workload.go
index a7423717f..4437f615e 100644
--- a/controllers/apps/component/transformer_component_workload.go
+++ b/controllers/apps/component/transformer_component_workload.go
@@ -353,6 +353,7 @@ func copyAndMergeITS(oldITS, newITS *workloads.InstanceSet) *workloads.InstanceS
 	itsObjCopy.Spec.PersistentVolumeClaimRetentionPolicy = itsProto.Spec.PersistentVolumeClaimRetentionPolicy
 	itsObjCopy.Spec.ParallelPodManagementConcurrency = itsProto.Spec.ParallelPodManagementConcurrency
 	itsObjCopy.Spec.PodUpdatePolicy = itsProto.Spec.PodUpdatePolicy
+	itsObjCopy.Spec.PodUpgradePolicy = itsProto.Spec.PodUpgradePolicy
 	itsObjCopy.Spec.InstanceUpdateStrategy = itsProto.Spec.InstanceUpdateStrategy
 	itsObjCopy.Spec.MemberUpdateStrategy = itsProto.Spec.MemberUpdateStrategy
 	itsObjCopy.Spec.Paused = itsProto.Spec.Paused
diff --git a/controllers/dataprotection/backup_controller.go b/controllers/dataprotection/backup_controller.go
index ee949fad2..8d86d50f4 100644
--- a/controllers/dataprotection/backup_controller.go
+++ b/controllers/dataprotection/backup_controller.go
@@ -286,10 +286,12 @@ func (r *BackupReconciler) handleDeletingPhase(reqCtx intctrlutil.RequestCtx, ba
 func (r *BackupReconciler) handleNewPhase(
 	reqCtx intctrlutil.RequestCtx,
 	backup *dpv1alpha1.Backup) (ctrl.Result, error) {
+
 	request, err := r.prepareBackupRequest(reqCtx, backup)
 	if err != nil {
 		return r.updateStatusIfFailed(reqCtx, backup.DeepCopy(), backup, err)
 	}
+
 	// record the status.target/status.targets infos for continuous backup.
 	if err = r.recordBackupStatusTargets(reqCtx, request); err != nil {
 		return r.updateStatusIfFailed(reqCtx, backup, request.Backup, err)
@@ -1043,6 +1045,10 @@ func setClusterSnapshotAnnotation(request *dpbackup.Request, cluster *kbappsv1.C
 
 // validateContinuousBackup validates the continuous backup.
 func validateContinuousBackup(backup *dpv1alpha1.Backup, reqCtx intctrlutil.RequestCtx, cli client.Client) error {
+	// filter the cluster that is creating when the backup is continuous
+	if clusterIsCreating(reqCtx, backup, cli) {
+		return intctrlutil.NewErrorf(intctrlutil.ErrorTypeRequeue, "requeue to wait for cluster %s creation to finish", backup.Labels[constant.AppInstanceLabelKey])
+	}
 	// validate if the continuous backup is created by a backupSchedule.
 	if _, ok := backup.Labels[dptypes.BackupScheduleLabelKey]; !ok {
 		return fmt.Errorf("continuous backup is only allowed to be created by backupSchedule")
@@ -1096,3 +1102,20 @@ func prepare4Incremental(request *dpbackup.Request) (*dpbackup.Request, error) {
 	}
 	return request, nil
 }
+
+// clusterIsCreating will return true when the backup is Continuous backup and the cluster is creating
+func clusterIsCreating(reqCtx intctrlutil.RequestCtx, backup *dpv1alpha1.Backup, cli client.Client) bool {
+	if backup.Labels[constant.AppInstanceLabelKey] != "" {
+		ownerCluster := &kbappsv1.Cluster{}
+		if err := cli.Get(reqCtx.Ctx, types.NamespacedName{
+			Namespace: backup.Namespace,
+			Name:      backup.Labels[constant.AppInstanceLabelKey],
+		}, ownerCluster); err != nil {
+			return false
+		}
+		if ownerCluster.Status.Phase == kbappsv1.CreatingClusterPhase {
+			return true
+		}
+	}
+	return false
+}
diff --git a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml
index 8a8da3186..1eb67e359 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_clusters.yaml
@@ -2832,6 +2832,7 @@ spec:
                           type: string
                       type: object
                     podUpdatePolicy:
+                      default: PreferInPlace
                       description: |-
                         PodUpdatePolicy indicates how pods should be updated
 
@@ -2845,6 +2846,16 @@ spec:
                       - StrictInPlace
                       - PreferInPlace
                       type: string
+                    podUpgradePolicy:
+                      description: |-
+                        PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+
+
+                        If not specified, the value of PodUpdatePolicy will be used.
+                      enum:
+                      - StrictInPlace
+                      - PreferInPlace
+                      type: string
                     replicas:
                       default: 1
                       description: Specifies the desired number of replicas in the
@@ -14071,6 +14082,7 @@ spec:
                               type: string
                           type: object
                         podUpdatePolicy:
+                          default: PreferInPlace
                           description: |-
                             PodUpdatePolicy indicates how pods should be updated
 
@@ -14084,6 +14096,16 @@ spec:
                           - StrictInPlace
                           - PreferInPlace
                           type: string
+                        podUpgradePolicy:
+                          description: |-
+                            PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+
+
+                            If not specified, the value of PodUpdatePolicy will be used.
+                          enum:
+                          - StrictInPlace
+                          - PreferInPlace
+                          type: string
                         replicas:
                           default: 1
                           description: Specifies the desired number of replicas in
diff --git a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml
index aba2cf748..c9be67383 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_componentdefinitions.yaml
@@ -10321,6 +10321,24 @@ spec:
                   - `Parallel`: Creates pods in parallel to match the desired scale without waiting. All pods are deleted at once
                   when scaling down.
                 type: string
+              podUpdatePolicy:
+                default: PreferInPlace
+                description: Specifies the default update policy for pods when the
+                  Component is updated.
+                enum:
+                - PreferInPlace
+                - ReCreate
+                type: string
+              podUpgradePolicy:
+                description: |-
+                  Specifies the default update policy for pods when the Component is upgraded (the service version changes).
+
+
+                  If not specified, the default behavior is the same as `podUpdatePolicy`.
+                enum:
+                - PreferInPlace
+                - ReCreate
+                type: string
               policyRules:
                 description: |-
                   Defines the namespaced policy rules required by the Component.
diff --git a/deploy/helm/crds/apps.kubeblocks.io_components.yaml b/deploy/helm/crds/apps.kubeblocks.io_components.yaml
index cfd53b868..7ed168a4b 100644
--- a/deploy/helm/crds/apps.kubeblocks.io_components.yaml
+++ b/deploy/helm/crds/apps.kubeblocks.io_components.yaml
@@ -2613,6 +2613,7 @@ spec:
                     type: string
                 type: object
               podUpdatePolicy:
+                default: PreferInPlace
                 description: |-
                   PodUpdatePolicy indicates how pods should be updated
 
@@ -2622,6 +2623,19 @@ spec:
                   - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod.
                   If that fails, it will fall back to the ReCreate, where pod will be recreated.
                   Default value is "PreferInPlace"
+                enum:
+                - StrictInPlace
+                - PreferInPlace
+                type: string
+              podUpgradePolicy:
+                description: |-
+                  PodUpgradePolicy indicates how pods should be updated when the component is upgraded.
+
+
+                  If not specified, the value of PodUpdatePolicy will be used.
+                enum:
+                - StrictInPlace
+                - PreferInPlace
                 type: string
               replicas:
                 default: 1
diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instances.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instances.yaml
index d4df9b9dc..f22ac00ce 100644
--- a/deploy/helm/crds/workloads.kubeblocks.io_instances.yaml
+++ b/deploy/helm/crds/workloads.kubeblocks.io_instances.yaml
@@ -1565,6 +1565,9 @@ spec:
               podUpdatePolicy:
                 description: PodUpdatePolicy indicates how pods should be updated.
                 type: string
+              podUpgradePolicy:
+                description: PodUpdatePolicy indicates how pods should be upgraded.
+                type: string
               roles:
                 description: A list of roles defined in the system. Instanceset obtains
                   role through pods' role label `kubeblocks.io/role`.
diff --git a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml
index 6b4d4660b..16e27bf44 100644
--- a/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml
+++ b/deploy/helm/crds/workloads.kubeblocks.io_instancesets.yaml
@@ -3094,15 +3094,10 @@ spec:
                   Note: This field will be removed in future version.
                 type: string
               podUpdatePolicy:
-                description: |-
-                  PodUpdatePolicy indicates how pods should be updated
-
-
-                  - `StrictInPlace` indicates that only allows in-place upgrades.
-                  Any attempt to modify other fields will be rejected.
-                  - `PreferInPlace` indicates that we will first attempt an in-place upgrade of the Pod.
-                  If that fails, it will fall back to the ReCreate, where pod will be recreated.
-                  Default value is "PreferInPlace"
+                description: PodUpdatePolicy indicates how pods should be updated.
+                type: string
+              podUpgradePolicy:
+                description: PodUpgradePolicy indicates how pods should be upgraded.
                 type: string
               replicas:
                 default: 1
diff --git a/deploy/helm/templates/kubeblocks-upgrade-hook.yaml b/deploy/helm/templates/kubeblocks-upgrade-hook.yaml
index 20dd1fd27..9482eb2f9 100644
--- a/deploy/helm/templates/kubeblocks-upgrade-hook.yaml
+++ b/deploy/helm/templates/kubeblocks-upgrade-hook.yaml
@@ -41,6 +41,10 @@ spec:
                  echo "ERROR: can not upgrade KubeBlocks from $version to {{ .Chart.AppVersion }}"
                  exit 1
               fi
+          {{- with .Values.securityContext }}
+          securityContext:
+            {{- toYaml . | nindent 12 }}
+          {{- end }}
       {{- with .Values.topologySpreadConstraints }}
       topologySpreadConstraints:
         {{- toYaml . | nindent 8 }}
diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md
index 56d3f3448..71a94bfd8 100644
--- a/docs/developer_docs/api-reference/cluster.md
+++ b/docs/developer_docs/api-reference/cluster.md
@@ -764,6 +764,21 @@ Default value is &ldquo;PreferInPlace&rdquo;</li>
 </tr>
 <tr>
 <td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpgradePolicy indicates how pods should be updated when the component is upgraded.</p>
+<p>If not specified, the value of PodUpdatePolicy will be used.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>instanceUpdateStrategy</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.InstanceUpdateStrategy">
@@ -1559,6 +1574,35 @@ when scaling down.</li>
 </tr>
 <tr>
 <td>
+<code>podUpdatePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Specifies the default update policy for pods when the Component is updated.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Specifies the default update policy for pods when the Component is upgraded (the service version changes).</p>
+<p>If not specified, the default behavior is the same as <code>podUpdatePolicy</code>.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>policyRules</code><br/>
 <em>
 <a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#policyrule-v1-rbac">
@@ -3332,6 +3376,21 @@ Default value is &ldquo;PreferInPlace&rdquo;</li>
 </tr>
 <tr>
 <td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpgradePolicy indicates how pods should be updated when the component is upgraded.</p>
+<p>If not specified, the value of PodUpdatePolicy will be used.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>instanceUpdateStrategy</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.InstanceUpdateStrategy">
@@ -5452,6 +5511,35 @@ when scaling down.</li>
 </tr>
 <tr>
 <td>
+<code>podUpdatePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Specifies the default update policy for pods when the Component is updated.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Specifies the default update policy for pods when the Component is upgraded (the service version changes).</p>
+<p>If not specified, the default behavior is the same as <code>podUpdatePolicy</code>.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>policyRules</code><br/>
 <em>
 <a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#policyrule-v1-rbac">
@@ -6556,6 +6644,21 @@ Default value is &ldquo;PreferInPlace&rdquo;</li>
 </tr>
 <tr>
 <td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpgradePolicy indicates how pods should be updated when the component is upgraded.</p>
+<p>If not specified, the value of PodUpdatePolicy will be used.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>instanceUpdateStrategy</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.InstanceUpdateStrategy">
@@ -9503,7 +9606,7 @@ More info: <a href="https://kubernetes.io/docs/concepts/storage/persistent-volum
 <h3 id="apps.kubeblocks.io/v1.PodUpdatePolicyType">PodUpdatePolicyType
 (<code>string</code> alias)</h3>
 <p>
-(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ClusterComponentSpec">ClusterComponentSpec</a>, <a href="#apps.kubeblocks.io/v1.ComponentSpec">ComponentSpec</a>, <a href="#workloads.kubeblocks.io/v1.InstanceSetSpec">InstanceSetSpec</a>, <a href="#workloads.kubeblocks.io/v1.InstanceSpec">InstanceSpec</a>)
+(<em>Appears on:</em><a href="#apps.kubeblocks.io/v1.ClusterComponentSpec">ClusterComponentSpec</a>, <a href="#apps.kubeblocks.io/v1.ComponentDefinitionSpec">ComponentDefinitionSpec</a>, <a href="#apps.kubeblocks.io/v1.ComponentSpec">ComponentSpec</a>, <a href="#workloads.kubeblocks.io/v1.InstanceSetSpec">InstanceSetSpec</a>, <a href="#workloads.kubeblocks.io/v1.InstanceSpec">InstanceSpec</a>)
 </p>
 <div>
 <p>PodUpdatePolicyType indicates how pods should be updated</p>
@@ -9519,6 +9622,9 @@ More info: <a href="https://kubernetes.io/docs/concepts/storage/persistent-volum
 <td><p>PreferInPlacePodUpdatePolicyType indicates that we will first attempt an in-place upgrade of the Pod.
 If that fails, it will fall back to the ReCreate, where pod will be recreated.</p>
 </td>
+</tr><tr><td><p>&#34;ReCreate&#34;</p></td>
+<td><p>ReCreatePodUpdatePolicyType indicates that it will always recreate the Pod.</p>
+</td>
 </tr><tr><td><p>&#34;StrictInPlace&#34;</p></td>
 <td><p>StrictInPlacePodUpdatePolicyType indicates that only allows in-place upgrades.
 Any attempt to modify other fields will be rejected.</p>
@@ -31557,6 +31663,20 @@ PodUpdatePolicyType
 </tr>
 <tr>
 <td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpdatePolicy indicates how pods should be upgraded.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>roles</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.ReplicaRole">
@@ -31916,14 +32036,21 @@ PodUpdatePolicyType
 </td>
 <td>
 <em>(Optional)</em>
-<p>PodUpdatePolicy indicates how pods should be updated</p>
-<ul>
-<li><code>StrictInPlace</code> indicates that only allows in-place upgrades.
-Any attempt to modify other fields will be rejected.</li>
-<li><code>PreferInPlace</code> indicates that we will first attempt an in-place upgrade of the Pod.
-If that fails, it will fall back to the ReCreate, where pod will be recreated.
-Default value is &ldquo;PreferInPlace&rdquo;</li>
-</ul>
+<p>PodUpdatePolicy indicates how pods should be updated.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpgradePolicy indicates how pods should be upgraded.</p>
 </td>
 </tr>
 <tr>
@@ -32575,14 +32702,21 @@ PodUpdatePolicyType
 </td>
 <td>
 <em>(Optional)</em>
-<p>PodUpdatePolicy indicates how pods should be updated</p>
-<ul>
-<li><code>StrictInPlace</code> indicates that only allows in-place upgrades.
-Any attempt to modify other fields will be rejected.</li>
-<li><code>PreferInPlace</code> indicates that we will first attempt an in-place upgrade of the Pod.
-If that fails, it will fall back to the ReCreate, where pod will be recreated.
-Default value is &ldquo;PreferInPlace&rdquo;</li>
-</ul>
+<p>PodUpdatePolicy indicates how pods should be updated.</p>
+</td>
+</tr>
+<tr>
+<td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpgradePolicy indicates how pods should be upgraded.</p>
 </td>
 </tr>
 <tr>
@@ -33131,6 +33265,20 @@ PodUpdatePolicyType
 </tr>
 <tr>
 <td>
+<code>podUpgradePolicy</code><br/>
+<em>
+<a href="#apps.kubeblocks.io/v1.PodUpdatePolicyType">
+PodUpdatePolicyType
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>PodUpdatePolicy indicates how pods should be upgraded.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>roles</code><br/>
 <em>
 <a href="#apps.kubeblocks.io/v1.ReplicaRole">
diff --git a/pkg/controller/builder/builder_component.go b/pkg/controller/builder/builder_component.go
index ea13a77ce..47531ebad 100644
--- a/pkg/controller/builder/builder_component.go
+++ b/pkg/controller/builder/builder_component.go
@@ -96,6 +96,11 @@ func (builder *ComponentBuilder) SetPodUpdatePolicy(policy *appsv1.PodUpdatePoli
 	return builder
 }
 
+func (builder *ComponentBuilder) SetPodUpgradePolicy(policy *appsv1.PodUpdatePolicyType) *ComponentBuilder {
+	builder.get().Spec.PodUpgradePolicy = policy
+	return builder
+}
+
 func (builder *ComponentBuilder) SetInstanceUpdateStrategy(strategy *appsv1.InstanceUpdateStrategy) *ComponentBuilder {
 	builder.get().Spec.InstanceUpdateStrategy = strategy
 	return builder
diff --git a/pkg/controller/builder/builder_instance.go b/pkg/controller/builder/builder_instance.go
index 30d5c6b84..4dac9abdb 100644
--- a/pkg/controller/builder/builder_instance.go
+++ b/pkg/controller/builder/builder_instance.go
@@ -196,6 +196,11 @@ func (builder *InstanceBuilder) SetPodUpdatePolicy(policy workloads.PodUpdatePol
 	return builder
 }
 
+func (builder *InstanceBuilder) SetPodUpgradePolicy(policy workloads.PodUpdatePolicyType) *InstanceBuilder {
+	builder.get().Spec.PodUpgradePolicy = policy
+	return builder
+}
+
 func (builder *InstanceBuilder) SetRoles(roles []workloads.ReplicaRole) *InstanceBuilder {
 	builder.get().Spec.Roles = roles
 	return builder
diff --git a/pkg/controller/builder/builder_instance_set.go b/pkg/controller/builder/builder_instance_set.go
index 247253ad6..691c1c3c5 100644
--- a/pkg/controller/builder/builder_instance_set.go
+++ b/pkg/controller/builder/builder_instance_set.go
@@ -110,6 +110,11 @@ func (builder *InstanceSetBuilder) SetPodUpdatePolicy(policy workloads.PodUpdate
 	return builder
 }
 
+func (builder *InstanceSetBuilder) SetPodUpgradePolicy(policy workloads.PodUpdatePolicyType) *InstanceSetBuilder {
+	builder.get().Spec.PodUpgradePolicy = policy
+	return builder
+}
+
 func (builder *InstanceSetBuilder) SetInstanceUpdateStrategy(strategy *workloads.InstanceUpdateStrategy) *InstanceSetBuilder {
 	builder.get().Spec.InstanceUpdateStrategy = strategy
 	return builder
diff --git a/pkg/controller/component/component.go b/pkg/controller/component/component.go
index 4699f8598..cf70d545b 100644
--- a/pkg/controller/component/component.go
+++ b/pkg/controller/component/component.go
@@ -79,6 +79,7 @@ func BuildComponent(cluster *appsv1.Cluster, compSpec *appsv1.ClusterComponentSp
 		SetServiceAccountName(compSpec.ServiceAccountName).
 		SetParallelPodManagementConcurrency(compSpec.ParallelPodManagementConcurrency).
 		SetPodUpdatePolicy(compSpec.PodUpdatePolicy).
+		SetPodUpgradePolicy(compSpec.PodUpgradePolicy).
 		SetInstanceUpdateStrategy(compSpec.InstanceUpdateStrategy).
 		SetVolumeClaimTemplates(compSpec.VolumeClaimTemplates).
 		SetPVCRetentionPolicy(compSpec.PersistentVolumeClaimRetentionPolicy).
diff --git a/pkg/controller/component/synthesize_component.go b/pkg/controller/component/synthesize_component.go
index ac916c0c1..958cd583c 100644
--- a/pkg/controller/component/synthesize_component.go
+++ b/pkg/controller/component/synthesize_component.go
@@ -116,7 +116,8 @@ func BuildSynthesizedComponent(ctx context.Context, cli client.Reader,
 		Stop:                             comp.Spec.Stop,
 		PodManagementPolicy:              compDef.Spec.PodManagementPolicy,
 		ParallelPodManagementConcurrency: comp.Spec.ParallelPodManagementConcurrency,
-		PodUpdatePolicy:                  comp.Spec.PodUpdatePolicy,
+		PodUpdatePolicy:                  getPodUpdatePolicy(comp, compDef),
+		PodUpgradePolicy:                 getPodUpgradePolicy(comp, compDef),
 		UpdateStrategy:                   compDef.Spec.UpdateStrategy,
 		InstanceUpdateStrategy:           comp.Spec.InstanceUpdateStrategy,
 		EnableInstanceAPI:                comp.Spec.EnableInstanceAPI,
@@ -529,3 +530,32 @@ func buildRuntimeClassName(synthesizeComp *SynthesizedComponent, comp *appsv1.Co
 	}
 	synthesizeComp.PodSpec.RuntimeClassName = comp.Spec.RuntimeClassName
 }
+
+func getPodUpdatePolicy(comp *appsv1.Component, compDef *appsv1.ComponentDefinition) appsv1.PodUpdatePolicyType {
+	policy := compDef.Spec.PodUpdatePolicy
+	if policy != nil && *policy == appsv1.ReCreatePodUpdatePolicyType {
+		return appsv1.ReCreatePodUpdatePolicyType
+	}
+	if comp.Spec.PodUpdatePolicy != nil {
+		return *comp.Spec.PodUpdatePolicy
+	}
+	return appsv1.PreferInPlacePodUpdatePolicyType // default
+}
+
+func getPodUpgradePolicy(comp *appsv1.Component, compDef *appsv1.ComponentDefinition) appsv1.PodUpdatePolicyType {
+	policy := compDef.Spec.PodUpgradePolicy
+	if policy == nil {
+		policy = compDef.Spec.PodUpdatePolicy
+	}
+	if policy != nil && *policy == appsv1.ReCreatePodUpdatePolicyType {
+		return appsv1.ReCreatePodUpdatePolicyType
+	}
+	policy = comp.Spec.PodUpgradePolicy
+	if policy == nil {
+		policy = comp.Spec.PodUpdatePolicy
+	}
+	if policy != nil {
+		return *policy
+	}
+	return appsv1.PreferInPlacePodUpdatePolicyType // default
+}
diff --git a/pkg/controller/component/type.go b/pkg/controller/component/type.go
index e1c848df5..ee1df0e94 100644
--- a/pkg/controller/component/type.go
+++ b/pkg/controller/component/type.go
@@ -62,12 +62,13 @@ type SynthesizedComponent struct {
 	EnvFromSources                   []corev1.EnvFromSource                 `json:"envFromSources,omitempty"`
 	Instances                        []kbappsv1.InstanceTemplate            `json:"instances,omitempty"`
 	FlatInstanceOrdinal              bool
-	InstanceImages                   map[string]map[string]string        `json:"instanceImages,omitempty"`
-	OfflineInstances                 []string                            `json:"offlineInstances,omitempty"`
-	Roles                            []kbappsv1.ReplicaRole              `json:"roles,omitempty"`
-	PodManagementPolicy              *appsv1.PodManagementPolicyType     `json:"podManagementPolicy,omitempty"`
-	ParallelPodManagementConcurrency *intstr.IntOrString                 `json:"parallelPodManagementConcurrency,omitempty"`
-	PodUpdatePolicy                  *kbappsv1.PodUpdatePolicyType       `json:"podUpdatePolicy,omitempty"`
+	InstanceImages                   map[string]map[string]string    `json:"instanceImages,omitempty"`
+	OfflineInstances                 []string                        `json:"offlineInstances,omitempty"`
+	Roles                            []kbappsv1.ReplicaRole          `json:"roles,omitempty"`
+	PodManagementPolicy              *appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"`
+	ParallelPodManagementConcurrency *intstr.IntOrString             `json:"parallelPodManagementConcurrency,omitempty"`
+	PodUpdatePolicy                  kbappsv1.PodUpdatePolicyType    `json:"podUpdatePolicy,omitempty"`
+	PodUpgradePolicy                 kbappsv1.PodUpdatePolicyType
 	UpdateStrategy                   *kbappsv1.UpdateStrategy            `json:"updateStrategy,omitempty"`
 	InstanceUpdateStrategy           *kbappsv1.InstanceUpdateStrategy    `json:"instanceUpdateStrategy,omitempty"`
 	PolicyRules                      []rbacv1.PolicyRule                 `json:"policyRules,omitempty"`
diff --git a/pkg/controller/factory/builder.go b/pkg/controller/factory/builder.go
index 2fe6409c1..8851dbdcd 100644
--- a/pkg/controller/factory/builder.go
+++ b/pkg/controller/factory/builder.go
@@ -79,7 +79,8 @@ func BuildInstanceSet(synthesizedComp *component.SynthesizedComponent, compDef *
 		SetRoles(synthesizedComp.Roles).
 		SetPodManagementPolicy(getPodManagementPolicy(synthesizedComp)).
 		SetParallelPodManagementConcurrency(getParallelPodManagementConcurrency(synthesizedComp)).
-		SetPodUpdatePolicy(getPodUpdatePolicy(synthesizedComp)).
+		SetPodUpdatePolicy(synthesizedComp.PodUpdatePolicy).
+		SetPodUpgradePolicy(synthesizedComp.PodUpgradePolicy).
 		SetInstanceUpdateStrategy(getInstanceUpdateStrategy(synthesizedComp)).
 		SetMemberUpdateStrategy(getMemberUpdateStrategy(synthesizedComp)).
 		SetLifecycleActions(synthesizedComp.LifecycleActions).
@@ -192,13 +193,6 @@ func getParallelPodManagementConcurrency(synthesizedComp *component.SynthesizedC
 	return &intstr.IntOrString{Type: intstr.String, StrVal: "100%"} // default value
 }
 
-func getPodUpdatePolicy(synthesizedComp *component.SynthesizedComponent) workloads.PodUpdatePolicyType {
-	if synthesizedComp.PodUpdatePolicy != nil {
-		return *synthesizedComp.PodUpdatePolicy
-	}
-	return kbappsv1.PreferInPlacePodUpdatePolicyType // default value
-}
-
 func getInstanceUpdateStrategy(synthesizedComp *component.SynthesizedComponent) *workloads.InstanceUpdateStrategy {
 	// TODO: on-delete if the member update strategy is not null?
 	return synthesizedComp.InstanceUpdateStrategy
diff --git a/pkg/controller/instance/in_place_update_utils.go b/pkg/controller/instance/in_place_update_utils.go
index 64a4bc30f..a8cbe2fe4 100644
--- a/pkg/controller/instance/in_place_update_utils.go
+++ b/pkg/controller/instance/in_place_update_utils.go
@@ -36,9 +36,9 @@ import (
 type podUpdatePolicy string
 
 const (
-	noOpsPolicy         podUpdatePolicy = "NoOps"
-	recreatePolicy      podUpdatePolicy = "Recreate"
-	inPlaceUpdatePolicy podUpdatePolicy = "InPlaceUpdate"
+	noOpsPolicy         podUpdatePolicy = "noOps"
+	recreatePolicy      podUpdatePolicy = "recreate"
+	inPlaceUpdatePolicy podUpdatePolicy = "inPlaceUpdate"
 )
 
 func supportPodVerticalScaling() bool {
@@ -283,41 +283,53 @@ func equalResourcesInPlaceFields(old, new *corev1.Pod) bool {
 	return true
 }
 
-func getPodUpdatePolicy(inst *workloads.Instance, pod *corev1.Pod) (podUpdatePolicy, error) {
+func getPodUpdatePolicy(inst *workloads.Instance, pod *corev1.Pod) (podUpdatePolicy, workloads.PodUpdatePolicyType, error) {
+	newPod, err := buildInstancePod(inst, getPodRevision(pod))
+	if err != nil {
+		return noOpsPolicy, "", err
+	}
+
+	specUpdatePolicy := getPodUpdatePolicyInSpec(inst, pod, newPod)
 	if getPodRevision(pod) != inst.Status.UpdateRevision {
 		if len(inst.Status.UpdateRevision) == 0 {
-			return noOpsPolicy, nil
+			return noOpsPolicy, "", nil
 		}
-		return recreatePolicy, nil
+		return recreatePolicy, specUpdatePolicy, nil
 	}
 
-	newPod, err := buildInstancePod(inst, getPodRevision(pod))
-	if err != nil {
-		return noOpsPolicy, err
-	}
 	basicUpdate := !equalBasicInPlaceFields(pod, newPod)
 	if viper.GetBool(FeatureGateIgnorePodVerticalScaling) {
 		if basicUpdate {
-			return inPlaceUpdatePolicy, nil
+			return inPlaceUpdatePolicy, specUpdatePolicy, nil
 		}
-		return noOpsPolicy, nil
+		return noOpsPolicy, "", nil
 	}
 
 	resourceUpdate := !equalResourcesInPlaceFields(pod, newPod)
 	if resourceUpdate {
 		if supportPodVerticalScaling() {
-			return inPlaceUpdatePolicy, nil
+			return inPlaceUpdatePolicy, specUpdatePolicy, nil
 		}
-		return recreatePolicy, nil
+		return recreatePolicy, specUpdatePolicy, nil
 	}
 
 	if basicUpdate {
-		return inPlaceUpdatePolicy, nil
+		return inPlaceUpdatePolicy, specUpdatePolicy, nil
+	}
+	return noOpsPolicy, "", nil
+}
+
+func getPodUpdatePolicyInSpec(inst *workloads.Instance, old, new *corev1.Pod) workloads.PodUpdatePolicyType {
+	if !equalField(old.Spec.InitContainers, new.Spec.InitContainers) {
+		return inst.Spec.PodUpgradePolicy
+	}
+	if !equalField(old.Spec.Containers, new.Spec.Containers) {
+		return inst.Spec.PodUpgradePolicy
 	}
-	return noOpsPolicy, nil
+	return inst.Spec.PodUpdatePolicy
 }
 
 func isPodUpdated(inst *workloads.Instance, pod *corev1.Pod) (bool, error) {
-	policy, err := getPodUpdatePolicy(inst, pod)
+	policy, _, err := getPodUpdatePolicy(inst, pod)
 	return policy == noOpsPolicy, err
 }
diff --git a/pkg/controller/instance/reconciler_update.go b/pkg/controller/instance/reconciler_update.go
index 06ef7fe31..dc9150a9e 100644
--- a/pkg/controller/instance/reconciler_update.go
+++ b/pkg/controller/instance/reconciler_update.go
@@ -80,7 +80,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 	// treat old and Pending pod as a special case, as they can be updated without a consequence
 	// podUpdatePolicy is ignored here since in-place update for a pending pod doesn't make much sense.
 	for _, pod := range oldPodList {
-		updatePolicy, err := getPodUpdatePolicy(inst, pod)
+		updatePolicy, _, err := getPodUpdatePolicy(inst, pod)
 		if err != nil {
 			return kubebuilderx.Continue, err
 		}
@@ -120,11 +120,11 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 			break
 		}
 
-		updatePolicy, err := getPodUpdatePolicy(inst, pod)
+		updatePolicy, specUpdatePolicy, err := getPodUpdatePolicy(inst, pod)
 		if err != nil {
 			return kubebuilderx.Continue, err
 		}
-		if inst.Spec.PodUpdatePolicy == kbappsv1.StrictInPlacePodUpdatePolicyType && updatePolicy == recreatePolicy {
+		if updatePolicy == recreatePolicy && specUpdatePolicy == kbappsv1.StrictInPlacePodUpdatePolicyType {
 			message := fmt.Sprintf("Instance %s/%s blocks on update as the podUpdatePolicy is %s and the pod %s can not inplace update",
 				inst.Namespace, inst.Name, kbappsv1.StrictInPlacePodUpdatePolicyType, pod.Name)
 			if tree != nil && tree.EventRecorder != nil {
@@ -134,6 +134,9 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 			isBlocked = true
 			break
 		}
+		if updatePolicy == inPlaceUpdatePolicy && specUpdatePolicy == kbappsv1.ReCreatePodUpdatePolicyType {
+			updatePolicy = recreatePolicy
+		}
 		if updatePolicy == inPlaceUpdatePolicy {
 			newPod, err := buildInstancePod(inst, getPodRevision(pod))
 			if err != nil {
diff --git a/pkg/controller/instanceset/in_place_update_util.go b/pkg/controller/instanceset/in_place_update_util.go
index f309057b8..e0f86dcc6 100644
--- a/pkg/controller/instanceset/in_place_update_util.go
+++ b/pkg/controller/instanceset/in_place_update_util.go
@@ -35,12 +35,12 @@ import (
 	viper "github.com/apecloud/kubeblocks/pkg/viperx"
 )
 
-type PodUpdatePolicy string
+type podUpdatePolicy string
 
 const (
-	NoOpsPolicy         PodUpdatePolicy = "NoOps"
-	RecreatePolicy      PodUpdatePolicy = "Recreate"
-	InPlaceUpdatePolicy PodUpdatePolicy = "InPlaceUpdate"
+	noOpsPolicy         podUpdatePolicy = "noOps"
+	recreatePolicy      podUpdatePolicy = "recreate"
+	inPlaceUpdatePolicy podUpdatePolicy = "inPlaceUpdate"
 )
 
 func supportPodVerticalScaling() bool {
@@ -285,55 +285,67 @@ func equalResourcesInPlaceFields(old, new *corev1.Pod) bool {
 	return true
 }
 
-func getPodUpdatePolicy(its *workloads.InstanceSet, pod *corev1.Pod) (PodUpdatePolicy, error) {
+func getPodUpdatePolicy(its *workloads.InstanceSet, pod *corev1.Pod) (podUpdatePolicy, workloads.PodUpdatePolicyType, error) {
 	updateRevisions, err := GetRevisions(its.Status.UpdateRevisions)
 	if err != nil {
-		return NoOpsPolicy, err
-	}
-
-	if getPodRevision(pod) != updateRevisions[pod.Name] {
-		return RecreatePolicy, nil
+		return noOpsPolicy, "", err
 	}
 
 	itsExt, err := instancetemplate.BuildInstanceSetExt(its, nil)
 	if err != nil {
-		return NoOpsPolicy, err
+		return noOpsPolicy, "", err
 	}
 	templateList := instancetemplate.BuildInstanceTemplateExt(itsExt)
 	templateName, err := getTemplateNameByPod(itsExt, pod)
 	if err != nil {
-		return NoOpsPolicy, err
+		return noOpsPolicy, "", err
 	}
 	index := slices.IndexFunc(templateList, func(templateExt *instancetemplate.InstanceTemplateExt) bool {
 		return templateName == templateExt.Name
 	})
 	if index < 0 {
-		return NoOpsPolicy, fmt.Errorf("no corresponding template found for instance %s", pod.Name)
+		return noOpsPolicy, "", fmt.Errorf("no corresponding template found for instance %s", pod.Name)
 	}
 	newPod, err := buildInstancePodByTemplate(pod.Name, templateList[index], its, getPodRevision(pod))
 	if err != nil {
-		return NoOpsPolicy, err
+		return noOpsPolicy, "", err
 	}
+
+	specUpdatePolicy := getPodUpdatePolicyInSpec(its, pod, newPod)
+	if getPodRevision(pod) != updateRevisions[pod.Name] {
+		return recreatePolicy, specUpdatePolicy, nil
+	}
+
 	basicUpdate := !equalBasicInPlaceFields(pod, newPod)
 	if viper.GetBool(FeatureGateIgnorePodVerticalScaling) {
 		if basicUpdate {
-			return InPlaceUpdatePolicy, nil
+			return inPlaceUpdatePolicy, specUpdatePolicy, nil
 		}
-		return NoOpsPolicy, nil
+		return noOpsPolicy, "", nil
 	}
 
 	resourceUpdate := !equalResourcesInPlaceFields(pod, newPod)
 	if resourceUpdate {
 		if supportPodVerticalScaling() {
-			return InPlaceUpdatePolicy, nil
+			return inPlaceUpdatePolicy, specUpdatePolicy, nil
 		}
-		return RecreatePolicy, nil
+		return recreatePolicy, specUpdatePolicy, nil
 	}
 
 	if basicUpdate {
-		return InPlaceUpdatePolicy, nil
+		return inPlaceUpdatePolicy, specUpdatePolicy, nil
 	}
-	return NoOpsPolicy, nil
+	return noOpsPolicy, "", nil
+}
+
+func getPodUpdatePolicyInSpec(its *workloads.InstanceSet, old, new *corev1.Pod) workloads.PodUpdatePolicyType {
+	if !equalField(old.Spec.InitContainers, new.Spec.InitContainers) {
+		return its.Spec.PodUpgradePolicy
+	}
+	if !equalField(old.Spec.Containers, new.Spec.Containers) {
+		return its.Spec.PodUpgradePolicy
+	}
+	return its.Spec.PodUpdatePolicy
 }
 
 func getTemplateNameByPod(itsExt *instancetemplate.InstanceSetExt, pod *corev1.Pod) (string, error) {
@@ -345,13 +357,17 @@ func getTemplateNameByPod(itsExt *instancetemplate.InstanceSetExt, pod *corev1.P
 	if err != nil {
 		return "", err
 	}
-	return nameToTemplateMap[pod.Name].Name, nil
+	tplExt, ok := nameToTemplateMap[pod.Name]
+	if ok {
+		return tplExt.Name, nil
+	}
+	return "", fmt.Errorf("no template found for pod %s/%s", pod.Namespace, pod.Name)
 }
 
 // IsPodUpdated tells whether the pod's spec is as expected in the InstanceSet.
 // This function is meant to replace the old fashion `GetPodRevision(pod) == updateRevision`,
 // as the pod template revision has been redefined in instanceset.
 func IsPodUpdated(its *workloads.InstanceSet, pod *corev1.Pod) (bool, error) {
-	policy, err := getPodUpdatePolicy(its, pod)
-	return policy == NoOpsPolicy, err
+	policy, _, err := getPodUpdatePolicy(its, pod)
+	return policy == noOpsPolicy, err
 }
diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go
index 6223202af..9f859aae5 100644
--- a/pkg/controller/instanceset/reconciler_update.go
+++ b/pkg/controller/instanceset/reconciler_update.go
@@ -138,11 +138,11 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 	// treat old and Pending pod as a special case, as they can be updated without a consequence
 	// PodUpdatePolicy is ignored here since in-place update for a pending pod doesn't make much sense.
 	for _, pod := range oldPodList {
-		updatePolicy, err := getPodUpdatePolicy(its, pod)
+		updatePolicy, _, err := getPodUpdatePolicy(its, pod)
 		if err != nil {
 			return kubebuilderx.Continue, err
 		}
-		if isPodPending(pod) && updatePolicy != NoOpsPolicy {
+		if isPodPending(pod) && updatePolicy != noOpsPolicy {
 			err = tree.Delete(pod)
 			// wait another reconciliation, so that the following update process won't be confused
 			return kubebuilderx.Continue, err
@@ -184,11 +184,11 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 			break
 		}
 
-		updatePolicy, err := getPodUpdatePolicy(its, pod)
+		updatePolicy, specUpdatePolicy, err := getPodUpdatePolicy(its, pod)
 		if err != nil {
 			return kubebuilderx.Continue, err
 		}
-		if its.Spec.PodUpdatePolicy == kbappsv1.StrictInPlacePodUpdatePolicyType && updatePolicy == RecreatePolicy {
+		if updatePolicy == recreatePolicy && specUpdatePolicy == kbappsv1.StrictInPlacePodUpdatePolicyType {
 			message := fmt.Sprintf("InstanceSet %s/%s blocks on update as the PodUpdatePolicy is %s and the pod %s can not inplace update",
 				its.Namespace, its.Name, kbappsv1.StrictInPlacePodUpdatePolicyType, pod.Name)
 			if tree != nil && tree.EventRecorder != nil {
@@ -198,7 +198,10 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 			isBlocked = true
 			break
 		}
-		if updatePolicy == InPlaceUpdatePolicy {
+		if updatePolicy == inPlaceUpdatePolicy && specUpdatePolicy == kbappsv1.ReCreatePodUpdatePolicyType {
+			updatePolicy = recreatePolicy
+		}
+		if updatePolicy == inPlaceUpdatePolicy {
 			newPod, err := buildInstancePodByTemplate(pod.Name, nameToTemplateMap[pod.Name], its, getPodRevision(pod))
 			if err != nil {
 				return kubebuilderx.Continue, err
@@ -224,7 +227,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 				return kubebuilderx.Continue, err
 			}
 			updatingPods++
-		} else if updatePolicy == RecreatePolicy {
+		} else if updatePolicy == recreatePolicy {
 			if !isTerminating(pod) {
 				if err = r.switchover(tree, its, pod); err != nil {
 					return kubebuilderx.Continue, err
@@ -237,7 +240,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
 		}
 
 		// actively reload the new configuration when the pod or container has not been updated
-		if updatePolicy == NoOpsPolicy {
+		if updatePolicy == noOpsPolicy {
 			allUpdated, err := r.reconfigure(tree, its, pod)
 			if err != nil {
 				return kubebuilderx.Continue, err
@@ -387,11 +390,11 @@ func (r *updateReconciler) setInstanceConfigStatus(its *workloads.InstanceSet, p
 }
 
 func (r *updateReconciler) isPodOrConfigUpdated(its *workloads.InstanceSet, pod *corev1.Pod) (bool, error) {
-	policy, err := getPodUpdatePolicy(its, pod)
+	policy, _, err := getPodUpdatePolicy(its, pod)
 	if err != nil {
 		return false, err
 	}
-	if policy != NoOpsPolicy {
+	if policy != noOpsPolicy {
 		return false, nil
 	}
 	for _, config := range its.Spec.Configs {
diff --git a/pkg/controller/instanceset2/instance_util.go b/pkg/controller/instanceset2/instance_util.go
index d18f5d94e..4e33c40ba 100644
--- a/pkg/controller/instanceset2/instance_util.go
+++ b/pkg/controller/instanceset2/instance_util.go
@@ -136,6 +136,7 @@ func buildInstanceByTemplate(tree *kubebuilderx.ObjectTree,
 		SetInstanceTemplateName(template.Name).
 		SetInstanceUpdateStrategyType(its.Spec.InstanceUpdateStrategy).
 		SetPodUpdatePolicy(its.Spec.PodUpdatePolicy).
+		SetPodUpgradePolicy(its.Spec.PodUpgradePolicy).
 		SetRoles(its.Spec.Roles).
 		SetMembershipReconfiguration(its.Spec.MembershipReconfiguration).
 		SetTemplateVars(its.Spec.TemplateVars)
@@ -309,6 +310,7 @@ func copyAndMergeInstance(oldInst, newInst *workloads.Instance) *workloads.Insta
 	targetInst.Spec.InstanceTemplateName = newInst.Spec.InstanceTemplateName
 	targetInst.Spec.InstanceUpdateStrategyType = newInst.Spec.InstanceUpdateStrategyType
 	targetInst.Spec.PodUpdatePolicy = newInst.Spec.PodUpdatePolicy
+	targetInst.Spec.PodUpgradePolicy = newInst.Spec.PodUpgradePolicy
 	targetInst.Spec.Roles = newInst.Spec.Roles
 	// targetInst.Spec.MembershipReconfiguration = newInst.Spec.MembershipReconfiguration
 	targetInst.Spec.TemplateVars = newInst.Spec.TemplateVars
diff --git a/pkg/dataprotection/backup/utils.go b/pkg/dataprotection/backup/utils.go
index bc611eb32..22dbc4e25 100644
--- a/pkg/dataprotection/backup/utils.go
+++ b/pkg/dataprotection/backup/utils.go
@@ -30,6 +30,7 @@ import (
 	"golang.org/x/mod/semver"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/utils/pointer"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -335,8 +336,10 @@ func StopStatefulSetsWhenFailed(ctx context.Context, cli client.Client, backup *
 	}
 	sts := &appsv1.StatefulSet{}
 	stsName := GenerateBackupStatefulSetName(backup, targetName, BackupDataJobNamePrefix)
-	if err := cli.Get(ctx, client.ObjectKey{Name: stsName, Namespace: backup.Namespace}, sts); client.IgnoreNotFound(err) != nil {
+	if err := cli.Get(ctx, client.ObjectKey{Name: stsName, Namespace: backup.Namespace}, sts); apierrors.IsNotFound(err) {
 		return nil
+	} else if err != nil {
+		return err
 	}
 	sts.Spec.Replicas = pointer.Int32(0)
 	return cli.Update(ctx, sts)



Test patch:
diff --git a/pkg/controller/builder/builder_instance_set_test.go b/pkg/controller/builder/builder_instance_set_test.go
index dba180754..dd265dff6 100644
--- a/pkg/controller/builder/builder_instance_set_test.go
+++ b/pkg/controller/builder/builder_instance_set_test.go
@@ -47,6 +47,7 @@ var _ = Describe("instance_set builder", func() {
 			port                         = int32(12345)
 			policy                       = appsv1.OrderedReadyPodManagement
 			podUpdatePolicy              = kbappsv1.PreferInPlacePodUpdatePolicyType
+			podUpgradePolicy             = kbappsv1.ReCreatePodUpdatePolicyType
 		)
 		parallelPodManagementConcurrency := &intstr.IntOrString{Type: intstr.String, StrVal: "100%"}
 		selectors := map[string]string{selectorKey1: selectorValue1, selectorKey2: selectorValue2, selectorKey3: selectorValue3, selectorKey4: selectorValue4}
@@ -131,6 +132,7 @@ var _ = Describe("instance_set builder", func() {
 			SetPodManagementPolicy(policy).
 			SetParallelPodManagementConcurrency(parallelPodManagementConcurrency).
 			SetPodUpdatePolicy(podUpdatePolicy).
+			SetPodUpgradePolicy(podUpgradePolicy).
 			SetInstanceUpdateStrategy(&strategy).
 			SetMemberUpdateStrategy(&memberUpdateStrategy).
 			SetPaused(paused).
@@ -157,6 +159,7 @@ var _ = Describe("instance_set builder", func() {
 		Expect(its.Spec.PodManagementPolicy).Should(Equal(policy))
 		Expect(its.Spec.ParallelPodManagementConcurrency).Should(Equal(parallelPodManagementConcurrency))
 		Expect(its.Spec.PodUpdatePolicy).Should(Equal(podUpdatePolicy))
+		Expect(its.Spec.PodUpgradePolicy).Should(Equal(podUpgradePolicy))
 		Expect(its.Spec.InstanceUpdateStrategy).ShouldNot(BeNil())
 		Expect(its.Spec.InstanceUpdateStrategy.RollingUpdate).ShouldNot(BeNil())
 		Expect(its.Spec.InstanceUpdateStrategy.RollingUpdate.Replicas).ShouldNot(BeNil())
diff --git a/pkg/controller/instanceset/in_place_update_util_test.go b/pkg/controller/instanceset/in_place_update_util_test.go
index d23fd8f9d..872583c9f 100644
--- a/pkg/controller/instanceset/in_place_update_util_test.go
+++ b/pkg/controller/instanceset/in_place_update_util_test.go
@@ -32,6 +32,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/rand"
 
+	kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
 	"github.com/apecloud/kubeblocks/pkg/constant"
 	"github.com/apecloud/kubeblocks/pkg/controller/builder"
 	"github.com/apecloud/kubeblocks/pkg/controller/kubebuilderx"
@@ -113,6 +114,8 @@ var _ = Describe("instance util test", func() {
 				SetMinReadySeconds(minReadySeconds).
 				SetRoles(roles).
 				SetPodManagementPolicy(appsv1.ParallelPodManagement).
+				SetPodUpdatePolicy(kbappsv1.ReCreatePodUpdatePolicyType).
+				SetPodUpgradePolicy(kbappsv1.PreferInPlacePodUpdatePolicyType).
 				GetObject()
 			tree := kubebuilderx.NewObjectTree()
 			tree.SetRoot(its)
@@ -138,9 +141,10 @@ var _ = Describe("instance util test", func() {
 			Expect(objects).Should(HaveLen(3))
 			pod1, ok := objects[0].(*corev1.Pod)
 			Expect(ok).Should(BeTrue())
-			policy, err := getPodUpdatePolicy(its, pod1)
+			policy, specPolicy, err := getPodUpdatePolicy(its, pod1)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(NoOpsPolicy))
+			Expect(policy).Should(Equal(noOpsPolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.PodUpdatePolicyType("")))
 
 			By("build a pod with revision updated")
 			pod2 := pod1.DeepCopy()
@@ -157,17 +161,19 @@ var _ = Describe("instance util test", func() {
 			})
 			pod2.Labels[appsv1.ControllerRevisionHashLabelKey] = "new-revision"
 			its.Status.UpdateRevisions[pod2.Name] = getPodRevision(pod2)
-			policy, err = getPodUpdatePolicy(its, pod2)
+			policy, specPolicy, err = getPodUpdatePolicy(its, pod2)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(RecreatePolicy))
+			Expect(policy).Should(Equal(recreatePolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.PreferInPlacePodUpdatePolicyType))
 
 			By("build a pod without revision updated, with basic mutable fields updated")
 			pod3 := pod1.DeepCopy()
 			randStr = rand.String(16)
 			mergeMap(&map[string]string{key: randStr}, &pod3.Annotations)
-			policy, err = getPodUpdatePolicy(its, pod3)
+			policy, specPolicy, err = getPodUpdatePolicy(its, pod3)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(InPlaceUpdatePolicy))
+			Expect(policy).Should(Equal(inPlaceUpdatePolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.ReCreatePodUpdatePolicyType))
 
 			By("build a pod without revision updated, with basic mutable and resources fields updated")
 			pod4 := pod3.DeepCopy()
@@ -176,9 +182,10 @@ var _ = Describe("instance util test", func() {
 				corev1.ResourceCPU: resource.MustParse(fmt.Sprintf("%dm", randInt)),
 			}
 			pod4.Spec.Containers[0].Resources.Requests = requests
-			policy, err = getPodUpdatePolicy(its, pod4)
+			policy, specPolicy, err = getPodUpdatePolicy(its, pod4)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(RecreatePolicy))
+			Expect(policy).Should(Equal(recreatePolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.ReCreatePodUpdatePolicyType))
 
 			By("build a pod without revision updated, with resources fields updated")
 			pod5 := pod1.DeepCopy()
@@ -187,17 +194,19 @@ var _ = Describe("instance util test", func() {
 				corev1.ResourceCPU: resource.MustParse(fmt.Sprintf("%dm", randInt)),
 			}
 			pod5.Spec.Containers[0].Resources.Requests = requests
-			policy, err = getPodUpdatePolicy(its, pod5)
+			policy, specPolicy, err = getPodUpdatePolicy(its, pod5)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(RecreatePolicy))
+			Expect(policy).Should(Equal(recreatePolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.ReCreatePodUpdatePolicyType))
 
 			By("build a pod without revision updated, with resources fields updated, with IgnorePodVerticalScaling enabled")
 			ignorePodVerticalScaling := viper.GetBool(FeatureGateIgnorePodVerticalScaling)
 			defer viper.Set(FeatureGateIgnorePodVerticalScaling, ignorePodVerticalScaling)
 			viper.Set(FeatureGateIgnorePodVerticalScaling, true)
-			policy, err = getPodUpdatePolicy(its, pod5)
+			policy, specPolicy, err = getPodUpdatePolicy(its, pod5)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(NoOpsPolicy))
+			Expect(policy).Should(Equal(noOpsPolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.PodUpdatePolicyType("")))
 
 			By("simulating a webhook to add an unknown container to the pod, with IgnorePodVerticalScaling disabled, it should use the in-place update policy")
 			pod6 := pod1.DeepCopy()
@@ -217,9 +226,10 @@ var _ = Describe("instance util test", func() {
 			ignorePodVerticalScaling = viper.GetBool(FeatureGateIgnorePodVerticalScaling)
 			defer viper.Set(FeatureGateIgnorePodVerticalScaling, ignorePodVerticalScaling)
 			viper.Set(FeatureGateIgnorePodVerticalScaling, false)
-			policy, err = getPodUpdatePolicy(its, pod6)
+			policy, specPolicy, err = getPodUpdatePolicy(its, pod6)
 			Expect(err).Should(BeNil())
-			Expect(policy).Should(Equal(InPlaceUpdatePolicy))
+			Expect(policy).Should(Equal(inPlaceUpdatePolicy))
+			Expect(specPolicy).Should(Equal(kbappsv1.PreferInPlacePodUpdatePolicyType))
 		})
 	})
 })