diff --git a/cumulus/pallets/xcmp-queue/src/migration.rs b/cumulus/pallets/xcmp-queue/src/migration.rs
index c7fa61a3e3f0513e53d67fcee4efd6c3cecf29f6..1702cd70bc2fb7e09cf15fbe21294bdb76b96ef1 100644
--- a/cumulus/pallets/xcmp-queue/src/migration.rs
+++ b/cumulus/pallets/xcmp-queue/src/migration.rs
@@ -20,7 +20,7 @@ use crate::{Config, OverweightIndex, Pallet, QueueConfig, QueueConfigData, DEFAU
 use cumulus_primitives_core::XcmpMessageFormat;
 use frame_support::{
 	pallet_prelude::*,
-	traits::{EnqueueMessage, OnRuntimeUpgrade, StorageVersion},
+	traits::{EnqueueMessage, StorageVersion, UncheckedOnRuntimeUpgrade},
 	weights::{constants::WEIGHT_REF_TIME_PER_MILLIS, Weight},
 };
 
@@ -96,7 +96,7 @@ pub mod v2 {
 	/// 2D weights).
 	pub struct UncheckedMigrationToV2<T: Config>(PhantomData<T>);
 
-	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrationToV2<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV2<T> {
 		#[allow(deprecated)]
 		fn on_runtime_upgrade() -> Weight {
 			let translate = |pre: v1::QueueConfigData| -> v2::QueueConfigData {
@@ -187,7 +187,7 @@ pub mod v3 {
 	/// Migrates the pallet storage to v3.
 	pub struct UncheckedMigrationToV3<T: Config>(PhantomData<T>);
 
-	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrationToV3<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV3<T> {
 		fn on_runtime_upgrade() -> Weight {
 			#[frame_support::storage_alias]
 			type Overweight<T: Config> =
@@ -266,7 +266,7 @@ pub mod v4 {
 	/// thresholds to at least the default values.
 	pub struct UncheckedMigrationToV4<T: Config>(PhantomData<T>);
 
-	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrationToV4<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV4<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let translate = |pre: v2::QueueConfigData| -> QueueConfigData {
 				let pre_default = v2::QueueConfigData::default();
@@ -315,6 +315,7 @@ pub mod v4 {
 mod tests {
 	use super::*;
 	use crate::mock::{new_test_ext, Test};
+	use frame_support::traits::OnRuntimeUpgrade;
 
 	#[test]
 	#[allow(deprecated)]
diff --git a/polkadot/runtime/common/src/assigned_slots/migration.rs b/polkadot/runtime/common/src/assigned_slots/migration.rs
index def6bad692a235d3282774b0db3fabdaf33c89b3..7e582dfa59635b180d2af55c9c6a4ef1cbae5468 100644
--- a/polkadot/runtime/common/src/assigned_slots/migration.rs
+++ b/polkadot/runtime/common/src/assigned_slots/migration.rs
@@ -15,7 +15,7 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use super::{Config, MaxPermanentSlots, MaxTemporarySlots, Pallet, LOG_TARGET};
-use frame_support::traits::{Get, GetStorageVersion, OnRuntimeUpgrade};
+use frame_support::traits::{Get, GetStorageVersion, UncheckedOnRuntimeUpgrade};
 
 #[cfg(feature = "try-runtime")]
 use frame_support::ensure;
@@ -23,10 +23,9 @@ use frame_support::ensure;
 use sp_std::vec::Vec;
 
 pub mod v1 {
-
 	use super::*;
 	pub struct VersionUncheckedMigrateToV1<T>(sp_std::marker::PhantomData<T>);
-	impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
 		#[cfg(feature = "try-runtime")]
 		fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 			let on_chain_version = Pallet::<T>::on_chain_storage_version();
diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs
index f977674a1e4e22254bcac6e68cc7bdf20711a043..18bb6bbfb559a23e44109230ad7fd87d0b2682ca 100644
--- a/polkadot/runtime/common/src/paras_registrar/migration.rs
+++ b/polkadot/runtime/common/src/paras_registrar/migration.rs
@@ -15,7 +15,7 @@
 // along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
 
 use super::*;
-use frame_support::traits::{Contains, OnRuntimeUpgrade};
+use frame_support::traits::{Contains, UncheckedOnRuntimeUpgrade};
 
 #[derive(Encode, Decode)]
 pub struct ParaInfoV1<Account, Balance> {
@@ -27,7 +27,7 @@ pub struct ParaInfoV1<Account, Balance> {
 pub struct VersionUncheckedMigrateToV1<T, UnlockParaIds>(
 	sp_std::marker::PhantomData<(T, UnlockParaIds)>,
 );
-impl<T: Config, UnlockParaIds: Contains<ParaId>> OnRuntimeUpgrade
+impl<T: Config, UnlockParaIds: Contains<ParaId>> UncheckedOnRuntimeUpgrade
 	for VersionUncheckedMigrateToV1<T, UnlockParaIds>
 {
 	fn on_runtime_upgrade() -> Weight {
diff --git a/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs b/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs
index 5071653377d49cb80d9af73375ead355e81ade01..8589ddc292bdd6b886934341b758cc1b9ed2d1aa 100644
--- a/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs
+++ b/polkadot/runtime/parachains/src/assigner_on_demand/migration.rs
@@ -18,7 +18,7 @@
 use super::*;
 use frame_support::{
 	migrations::VersionedMigration, pallet_prelude::ValueQuery, storage_alias,
-	traits::OnRuntimeUpgrade, weights::Weight,
+	traits::UncheckedOnRuntimeUpgrade, weights::Weight,
 };
 
 mod v0 {
@@ -51,7 +51,7 @@ mod v1 {
 
 	/// Migration to V1
 	pub struct UncheckedMigrateToV1<T>(sp_std::marker::PhantomData<T>);
-	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrateToV1<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV1<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let mut weight: Weight = Weight::zero();
 
@@ -141,7 +141,7 @@ pub type MigrateV0ToV1<T> = VersionedMigration<
 
 #[cfg(test)]
 mod tests {
-	use super::{v0, v1, OnRuntimeUpgrade, Weight};
+	use super::{v0, v1, UncheckedOnRuntimeUpgrade, Weight};
 	use crate::mock::{new_test_ext, MockGenesisConfig, OnDemandAssigner, Test};
 	use primitives::Id as ParaId;
 
@@ -163,7 +163,7 @@ mod tests {
 
 			// For tests, db weight is zero.
 			assert_eq!(
-				<v1::UncheckedMigrateToV1<Test> as OnRuntimeUpgrade>::on_runtime_upgrade(),
+				<v1::UncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
 				Weight::zero()
 			);
 
diff --git a/polkadot/runtime/parachains/src/configuration/migration/v10.rs b/polkadot/runtime/parachains/src/configuration/migration/v10.rs
index 3c8d6084ace72fa112551d4b29b43315afa6b8ed..fa72c357d7dab20dfee26fefa270e677f6b8549f 100644
--- a/polkadot/runtime/parachains/src/configuration/migration/v10.rs
+++ b/polkadot/runtime/parachains/src/configuration/migration/v10.rs
@@ -17,7 +17,11 @@
 //! A module that is responsible for migration of storage.
 
 use crate::configuration::{Config, Pallet};
-use frame_support::{pallet_prelude::*, traits::Defensive, weights::Weight};
+use frame_support::{
+	pallet_prelude::*,
+	traits::{Defensive, UncheckedOnRuntimeUpgrade},
+	weights::Weight,
+};
 use frame_system::pallet_prelude::BlockNumberFor;
 use primitives::{
 	AsyncBackingParams, Balance, ExecutorParams, NodeFeatures, SessionIndex,
@@ -26,8 +30,6 @@ use primitives::{
 use sp_runtime::Perbill;
 use sp_std::vec::Vec;
 
-use frame_support::traits::OnRuntimeUpgrade;
-
 use super::v9::V9HostConfiguration;
 // All configuration of the runtime with respect to paras.
 #[derive(Clone, Encode, PartialEq, Decode, Debug)]
@@ -163,7 +165,7 @@ mod v10 {
 }
 
 pub struct VersionUncheckedMigrateToV10<T>(sp_std::marker::PhantomData<T>);
-impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateToV10<T> {
+impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV10<T> {
 	#[cfg(feature = "try-runtime")]
 	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 		log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade() for HostConfiguration MigrateToV10");
diff --git a/polkadot/runtime/parachains/src/configuration/migration/v11.rs b/polkadot/runtime/parachains/src/configuration/migration/v11.rs
index 7ed9d08688555e610528bfcff0b4591ec65f55c7..65656e8d7c065ac916dde9be670e8c3dfdbfc477 100644
--- a/polkadot/runtime/parachains/src/configuration/migration/v11.rs
+++ b/polkadot/runtime/parachains/src/configuration/migration/v11.rs
@@ -18,7 +18,10 @@
 
 use crate::configuration::{self, Config, Pallet};
 use frame_support::{
-	migrations::VersionedMigration, pallet_prelude::*, traits::Defensive, weights::Weight,
+	migrations::VersionedMigration,
+	pallet_prelude::*,
+	traits::{Defensive, UncheckedOnRuntimeUpgrade},
+	weights::Weight,
 };
 use frame_system::pallet_prelude::BlockNumberFor;
 use primitives::{
@@ -27,7 +30,6 @@ use primitives::{
 };
 use sp_std::vec::Vec;
 
-use frame_support::traits::OnRuntimeUpgrade;
 use polkadot_core_primitives::Balance;
 use sp_arithmetic::Perbill;
 
@@ -176,7 +178,7 @@ pub type MigrateToV11<T> = VersionedMigration<
 >;
 
 pub struct UncheckedMigrateToV11<T>(sp_std::marker::PhantomData<T>);
-impl<T: Config> OnRuntimeUpgrade for UncheckedMigrateToV11<T> {
+impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV11<T> {
 	#[cfg(feature = "try-runtime")]
 	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 		log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade() for HostConfiguration MigrateToV11");
diff --git a/polkadot/runtime/parachains/src/configuration/migration/v12.rs b/polkadot/runtime/parachains/src/configuration/migration/v12.rs
index 4295a79893e8ee52632d54772bd108b4de37cc3f..69bacc83d044670ed65c9b168c07ae9acca508a5 100644
--- a/polkadot/runtime/parachains/src/configuration/migration/v12.rs
+++ b/polkadot/runtime/parachains/src/configuration/migration/v12.rs
@@ -20,7 +20,7 @@ use crate::configuration::{self, migration::v11::V11HostConfiguration, Config, P
 use frame_support::{
 	migrations::VersionedMigration,
 	pallet_prelude::*,
-	traits::{Defensive, OnRuntimeUpgrade},
+	traits::{Defensive, UncheckedOnRuntimeUpgrade},
 };
 use frame_system::pallet_prelude::BlockNumberFor;
 use primitives::vstaging::SchedulerParams;
@@ -70,7 +70,7 @@ pub type MigrateToV12<T> = VersionedMigration<
 
 pub struct UncheckedMigrateToV12<T>(sp_std::marker::PhantomData<T>);
 
-impl<T: Config> OnRuntimeUpgrade for UncheckedMigrateToV12<T> {
+impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV12<T> {
 	#[cfg(feature = "try-runtime")]
 	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 		log::trace!(target: crate::configuration::LOG_TARGET, "Running pre_upgrade() for HostConfiguration MigrateToV12");
diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs
index 1e63b209f4e78de48ef349d90b6ef40d21afe124..5f35680ee694c45f519a1e31478c4aef0f7c19b9 100644
--- a/polkadot/runtime/parachains/src/inclusion/migration.rs
+++ b/polkadot/runtime/parachains/src/inclusion/migration.rs
@@ -73,7 +73,7 @@ mod v1 {
 		CandidatePendingAvailability as V1CandidatePendingAvailability, Config, Pallet,
 		PendingAvailability as V1PendingAvailability,
 	};
-	use frame_support::{traits::OnRuntimeUpgrade, weights::Weight};
+	use frame_support::{traits::UncheckedOnRuntimeUpgrade, weights::Weight};
 	use sp_core::Get;
 	use sp_std::{collections::vec_deque::VecDeque, vec::Vec};
 
@@ -87,7 +87,7 @@ mod v1 {
 
 	pub struct VersionUncheckedMigrateToV1<T>(sp_std::marker::PhantomData<T>);
 
-	impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
 		#[cfg(feature = "try-runtime")]
 		fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 			log::trace!(target: crate::inclusion::LOG_TARGET, "Running pre_upgrade() for inclusion MigrateToV1");
@@ -216,7 +216,7 @@ mod tests {
 		},
 		mock::{new_test_ext, MockGenesisConfig, Test},
 	};
-	use frame_support::traits::OnRuntimeUpgrade;
+	use frame_support::traits::UncheckedOnRuntimeUpgrade;
 	use primitives::{AvailabilityBitfield, Id as ParaId};
 	use test_helpers::{dummy_candidate_commitments, dummy_candidate_descriptor, dummy_hash};
 
@@ -225,7 +225,7 @@ mod tests {
 		new_test_ext(MockGenesisConfig::default()).execute_with(|| {
 			// No data to migrate.
 			assert_eq!(
-				<VersionUncheckedMigrateToV1<Test> as OnRuntimeUpgrade>::on_runtime_upgrade(),
+				<VersionUncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
 				Weight::zero()
 			);
 			assert!(V1PendingAvailability::<Test>::iter().next().is_none());
@@ -299,7 +299,7 @@ mod tests {
 
 			// For tests, db weight is zero.
 			assert_eq!(
-				<VersionUncheckedMigrateToV1<Test> as OnRuntimeUpgrade>::on_runtime_upgrade(),
+				<VersionUncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
 				Weight::zero()
 			);
 
diff --git a/polkadot/runtime/parachains/src/scheduler/migration.rs b/polkadot/runtime/parachains/src/scheduler/migration.rs
index c47fbab046fecd4ee56a52b976f1d9e6e861f217..b030940fb41da8c8b85bce4fb14cf68723924058 100644
--- a/polkadot/runtime/parachains/src/scheduler/migration.rs
+++ b/polkadot/runtime/parachains/src/scheduler/migration.rs
@@ -19,7 +19,7 @@
 use super::*;
 use frame_support::{
 	migrations::VersionedMigration, pallet_prelude::ValueQuery, storage_alias,
-	traits::OnRuntimeUpgrade, weights::Weight,
+	traits::UncheckedOnRuntimeUpgrade, weights::Weight,
 };
 
 /// Old/legacy assignment representation (v0).
@@ -105,7 +105,8 @@ mod v0 {
 // - Assignments only consist of `ParaId`, `Assignment` is a concrete type (Same as V0Assignment).
 mod v1 {
 	use frame_support::{
-		pallet_prelude::ValueQuery, storage_alias, traits::OnRuntimeUpgrade, weights::Weight,
+		pallet_prelude::ValueQuery, storage_alias, traits::UncheckedOnRuntimeUpgrade,
+		weights::Weight,
 	};
 	use frame_system::pallet_prelude::BlockNumberFor;
 
@@ -164,7 +165,7 @@ mod v1 {
 
 	/// Migration to V1
 	pub struct UncheckedMigrateToV1<T>(sp_std::marker::PhantomData<T>);
-	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrateToV1<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV1<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let mut weight: Weight = Weight::zero();
 
@@ -302,7 +303,7 @@ mod v2 {
 	/// Migration to V2
 	pub struct UncheckedMigrateToV2<T>(sp_std::marker::PhantomData<T>);
 
-	impl<T: Config> OnRuntimeUpgrade for UncheckedMigrateToV2<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV2<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let mut weight: Weight = Weight::zero();
 
diff --git a/polkadot/xcm/pallet-xcm/src/migration.rs b/polkadot/xcm/pallet-xcm/src/migration.rs
index 018436aa3c93a9291a99820a57118d119f5a0828..b157e6b5c3d5f27d206b73a1b040ca5d7944b8f2 100644
--- a/polkadot/xcm/pallet-xcm/src/migration.rs
+++ b/polkadot/xcm/pallet-xcm/src/migration.rs
@@ -19,7 +19,7 @@ use crate::{
 };
 use frame_support::{
 	pallet_prelude::*,
-	traits::{OnRuntimeUpgrade, StorageVersion},
+	traits::{OnRuntimeUpgrade, StorageVersion, UncheckedOnRuntimeUpgrade},
 	weights::Weight,
 };
 
@@ -35,7 +35,7 @@ pub mod v1 {
 	///
 	/// Use experimental [`MigrateToV1`] instead.
 	pub struct VersionUncheckedMigrateToV1<T>(sp_std::marker::PhantomData<T>);
-	impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let mut weight = T::DbWeight::get().reads(1);
 
diff --git a/prdoc/pr_3835.prdoc b/prdoc/pr_3835.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..d2f49f8fc1161da7f264ae2a55ebcfd462dcca27
--- /dev/null
+++ b/prdoc/pr_3835.prdoc
@@ -0,0 +1,54 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: "migrations: prevent accidentally using inner unversioned migration instead of `VersionedMigration`"
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      Currently, it is possible to accidentally use inner unversioned migration instead of `VersionedMigration` 
+      since both implement `OnRuntimeUpgrade`. With this change, we make it clear that `Inner` is not intended 
+      to be used directly. It is achieved by bounding `Inner` to new trait `UncheckedOnRuntimeUpgrade`, which 
+      has the same interface as `OnRuntimeUpgrade`, but can not be used directly for runtime upgrade migrations.
+      
+      This change will break all existing migrations passed to `VersionedMigration`. Developers should simply change 
+      those migrations to implement `UncheckedOnRuntimeUpgrade` instead of `OnRuntimeUpgrade`.
+
+      Example:
+
+      ```
+      --- a/path/to/migration.rs
+      +++ b/path/to/migration.rs
+      @@ -1,7 +1,7 @@
+      -impl<T: Config> OnRuntimeUpgrade for MigrateVNToVM<T> {
+      +impl<T: Config> UncheckedOnRuntimeUpgrade for MigrateVNToVM<T> {
+          fn on_runtime_upgrade() -> Weight {
+              // Migration logic here
+              // Adjust the migration logic if necessary to align with the expectations
+              // of new `UncheckedOnRuntimeUpgrade` trait.
+              0
+          }
+      }
+      ```
+
+crates:
+  - name: "pallet-example-single-block-migrations"
+    bump: "major"
+  - name: "pallet-xcm"
+    bump: "major"
+  - name: "pallet-grandpa"
+    bump: "major"
+  - name: "pallet-identity"
+    bump: "major"
+  - name: "pallet-nomination-pools"
+    bump: "major"
+  - name: "pallet-society"
+    bump: "major"
+  - name: "frame-support"
+    bump: "major"
+  - name: "pallet-uniques"
+    bump: "major"
+  - name: "polkadot-runtime-parachains"
+    bump: "major"
+  - name: "polkadot-runtime-common"
+    bump: "major"
diff --git a/substrate/frame/examples/single-block-migrations/src/lib.rs b/substrate/frame/examples/single-block-migrations/src/lib.rs
index b36d52622678d85659692e6bbd5b439899e383f9..411537aa8c65f0c1462336cd6705a2a900bc230a 100644
--- a/substrate/frame/examples/single-block-migrations/src/lib.rs
+++ b/substrate/frame/examples/single-block-migrations/src/lib.rs
@@ -89,7 +89,7 @@
 //!
 //! See the migration source code for detailed comments.
 //!
-//! To keep the migration logic organised, it is split across additional modules:
+//! Here's a brief overview of modules and types defined in `v1.rs`:
 //!
 //! ### `mod v0`
 //!
@@ -98,28 +98,29 @@
 //!
 //! This allows reading the old v0 value from storage during the migration.
 //!
-//! ### `mod version_unchecked`
+//! ### `InnerMigrateV0ToV1`
 //!
 //! Here we define our raw migration logic,
-//! `version_unchecked::MigrateV0ToV1` which implements the [`OnRuntimeUpgrade`] trait.
+//! `InnerMigrateV0ToV1` which implements the [`UncheckedOnRuntimeUpgrade`] trait.
 //!
-//! Importantly, it is kept in a private module so that it cannot be accidentally used in a runtime.
+//! #### Why [`UncheckedOnRuntimeUpgrade`]?
 //!
-//! Private modules cannot be referenced in docs, so please read the code directly.
+//! Otherwise, we would have two implementations of [`OnRuntimeUpgrade`] which could be confusing,
+//! and may lead to accidentally using the wrong one.
 //!
 //! #### Standalone Struct or Pallet Hook?
 //!
 //! Note that the storage migration logic is attached to a standalone struct implementing
-//! [`OnRuntimeUpgrade`], rather than implementing the
+//! [`UncheckedOnRuntimeUpgrade`], rather than implementing the
 //! [`Hooks::on_runtime_upgrade`](frame_support::traits::Hooks::on_runtime_upgrade) hook directly on
 //! the pallet. The pallet hook is better suited for special types of logic that need to execute on
 //! every runtime upgrade, but not so much for one-off storage migrations.
 //!
-//! ### `pub mod versioned`
+//! ### `MigrateV0ToV1`
 //!
-//! Here, `version_unchecked::MigrateV0ToV1` is wrapped in a
+//! Here, `InnerMigrateV0ToV1` is wrapped in a
 //! [`VersionedMigration`] to define
-//! [`versioned::MigrateV0ToV1`](crate::migrations::v1::versioned::MigrateV0ToV1), which may be used
+//! [`MigrateV0ToV1`](crate::migrations::v1::MigrateV0ToV1), which may be used
 //! in runtimes.
 //!
 //! Using [`VersionedMigration`] ensures that
@@ -128,8 +129,6 @@
 //! - Reads and writes from checking and setting the on-chain storage version are accounted for in
 //!   the final [`Weight`](frame_support::weights::Weight)
 //!
-//! This is the only public module exported from `v1`.
-//!
 //! ### `mod test`
 //!
 //! Here basic unit tests are defined for the migration.
@@ -142,7 +141,8 @@
 //! [`VersionedMigration`]: frame_support::migrations::VersionedMigration
 //! [`GetStorageVersion`]: frame_support::traits::GetStorageVersion
 //! [`OnRuntimeUpgrade`]: frame_support::traits::OnRuntimeUpgrade
-//! [`MigrateV0ToV1`]: crate::migrations::v1::versioned::MigrationV0ToV1
+//! [`UncheckedOnRuntimeUpgrade`]: frame_support::traits::UncheckedOnRuntimeUpgrade
+//! [`MigrateV0ToV1`]: crate::migrations::v1::MigrateV0ToV1
 
 // We make sure this pallet uses `no_std` for compiling to Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
diff --git a/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs
index b46640a320207551ab55b1dc793574ce68c4c26c..18ef4e72cc4f5ce938abf03d5eaea3039ec0b030 100644
--- a/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs
+++ b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs
@@ -17,7 +17,7 @@
 
 use frame_support::{
 	storage_alias,
-	traits::{Get, OnRuntimeUpgrade},
+	traits::{Get, UncheckedOnRuntimeUpgrade},
 };
 
 #[cfg(feature = "try-runtime")]
@@ -34,118 +34,92 @@ mod v0 {
 	pub type Value<T: crate::Config> = StorageValue<crate::Pallet<T>, u32>;
 }
 
-/// Private module containing *version unchecked* migration logic.
+/// Implements [`UncheckedOnRuntimeUpgrade`], migrating the state of this pallet from V0 to V1.
 ///
-/// Should only be used by the [`VersionedMigration`](frame_support::migrations::VersionedMigration)
-/// type in this module to create something to export.
+/// In V0 of the template [`crate::Value`] is just a `u32`. In V1, it has been upgraded to
+/// contain the struct [`crate::CurrentAndPreviousValue`].
 ///
-/// The unversioned migration should be kept private so the unversioned migration cannot
-/// accidentally be used in any runtimes.
-///
-/// For more about this pattern of keeping items private, see
-/// - <https://github.com/rust-lang/rust/issues/30905>
-/// - <https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40>
-mod version_unchecked {
-	use super::*;
+/// In this migration, update the on-chain storage for the pallet to reflect the new storage
+/// layout.
+pub struct InnerMigrateV0ToV1<T: crate::Config>(sp_std::marker::PhantomData<T>);
+
+impl<T: crate::Config> UncheckedOnRuntimeUpgrade for InnerMigrateV0ToV1<T> {
+	/// Return the existing [`crate::Value`] so we can check that it was correctly set in
+	/// `InnerMigrateV0ToV1::post_upgrade`.
+	#[cfg(feature = "try-runtime")]
+	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
+		use codec::Encode;
+
+		// Access the old value using the `storage_alias` type
+		let old_value = v0::Value::<T>::get();
+		// Return it as an encoded `Vec<u8>`
+		Ok(old_value.encode())
+	}
 
-	/// Implements [`OnRuntimeUpgrade`], migrating the state of this pallet from V0 to V1.
-	///
-	/// In V0 of the template [`crate::Value`] is just a `u32`. In V1, it has been upgraded to
-	/// contain the struct [`crate::CurrentAndPreviousValue`].
+	/// Migrate the storage from V0 to V1.
 	///
-	/// In this migration, update the on-chain storage for the pallet to reflect the new storage
-	/// layout.
-	pub struct MigrateV0ToV1<T: crate::Config>(sp_std::marker::PhantomData<T>);
-
-	impl<T: crate::Config> OnRuntimeUpgrade for MigrateV0ToV1<T> {
-		/// Return the existing [`crate::Value`] so we can check that it was correctly set in
-		/// `version_unchecked::MigrateV0ToV1::post_upgrade`.
-		#[cfg(feature = "try-runtime")]
-		fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
-			use codec::Encode;
-
-			// Access the old value using the `storage_alias` type
-			let old_value = v0::Value::<T>::get();
-			// Return it as an encoded `Vec<u8>`
-			Ok(old_value.encode())
-		}
-
-		/// Migrate the storage from V0 to V1.
-		///
-		/// - If the value doesn't exist, there is nothing to do.
-		/// - If the value exists, it is read and then written back to storage inside a
-		/// [`crate::CurrentAndPreviousValue`].
-		fn on_runtime_upgrade() -> frame_support::weights::Weight {
-			// Read the old value from storage
-			if let Some(old_value) = v0::Value::<T>::take() {
-				// Write the new value to storage
-				let new = crate::CurrentAndPreviousValue { current: old_value, previous: None };
-				crate::Value::<T>::put(new);
-				// One read for the old value, one write for the new value
-				T::DbWeight::get().reads_writes(1, 1)
-			} else {
-				// One read for trying to access the old value
-				T::DbWeight::get().reads(1)
-			}
+	/// - If the value doesn't exist, there is nothing to do.
+	/// - If the value exists, it is read and then written back to storage inside a
+	/// [`crate::CurrentAndPreviousValue`].
+	fn on_runtime_upgrade() -> frame_support::weights::Weight {
+		// Read the old value from storage
+		if let Some(old_value) = v0::Value::<T>::take() {
+			// Write the new value to storage
+			let new = crate::CurrentAndPreviousValue { current: old_value, previous: None };
+			crate::Value::<T>::put(new);
+			// One read for the old value, one write for the new value
+			T::DbWeight::get().reads_writes(1, 1)
+		} else {
+			// One read for trying to access the old value
+			T::DbWeight::get().reads(1)
 		}
+	}
 
-		/// Verifies the storage was migrated correctly.
-		///
-		/// - If there was no old value, the new value should not be set.
-		/// - If there was an old value, the new value should be a
-		///   [`crate::CurrentAndPreviousValue`].
-		#[cfg(feature = "try-runtime")]
-		fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
-			use codec::Decode;
-			use frame_support::ensure;
-
-			let maybe_old_value = Option::<u32>::decode(&mut &state[..]).map_err(|_| {
-				sp_runtime::TryRuntimeError::Other("Failed to decode old value from storage")
-			})?;
-
-			match maybe_old_value {
-				Some(old_value) => {
-					let expected_new_value =
-						crate::CurrentAndPreviousValue { current: old_value, previous: None };
-					let actual_new_value = crate::Value::<T>::get();
-
-					ensure!(actual_new_value.is_some(), "New value not set");
-					ensure!(
-						actual_new_value == Some(expected_new_value),
-						"New value not set correctly"
-					);
-				},
-				None => {
-					ensure!(crate::Value::<T>::get().is_none(), "New value unexpectedly set");
-				},
-			};
-			Ok(())
-		}
+	/// Verifies the storage was migrated correctly.
+	///
+	/// - If there was no old value, the new value should not be set.
+	/// - If there was an old value, the new value should be a [`crate::CurrentAndPreviousValue`].
+	#[cfg(feature = "try-runtime")]
+	fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
+		use codec::Decode;
+		use frame_support::ensure;
+
+		let maybe_old_value = Option::<u32>::decode(&mut &state[..]).map_err(|_| {
+			sp_runtime::TryRuntimeError::Other("Failed to decode old value from storage")
+		})?;
+
+		match maybe_old_value {
+			Some(old_value) => {
+				let expected_new_value =
+					crate::CurrentAndPreviousValue { current: old_value, previous: None };
+				let actual_new_value = crate::Value::<T>::get();
+
+				ensure!(actual_new_value.is_some(), "New value not set");
+				ensure!(
+					actual_new_value == Some(expected_new_value),
+					"New value not set correctly"
+				);
+			},
+			None => {
+				ensure!(crate::Value::<T>::get().is_none(), "New value unexpectedly set");
+			},
+		};
+		Ok(())
 	}
 }
 
-/// Public module containing *version checked* migration logic.
-///
-/// This is the only module that should be exported from this module.
-///
-/// See [`VersionedMigration`](frame_support::migrations::VersionedMigration) docs for more about
-/// how it works.
-pub mod versioned {
-	use super::*;
-
-	/// `version_unchecked::MigrateV0ToV1` wrapped in a
-	/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that:
-	/// - The migration only runs once when the on-chain storage version is 0
-	/// - The on-chain storage version is updated to `1` after the migration executes
-	/// - Reads/Writes from checking/settings the on-chain storage version are accounted for
-	pub type MigrateV0ToV1<T> = frame_support::migrations::VersionedMigration<
-		0, // The migration will only execute when the on-chain storage version is 0
-		1, // The on-chain storage version will be set to 1 after the migration is complete
-		version_unchecked::MigrateV0ToV1<T>,
-		crate::pallet::Pallet<T>,
-		<T as frame_system::Config>::DbWeight,
-	>;
-}
+/// [`UncheckedOnRuntimeUpgrade`] implementation [`InnerMigrateV0ToV1`] wrapped in a
+/// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that:
+/// - The migration only runs once when the on-chain storage version is 0
+/// - The on-chain storage version is updated to `1` after the migration executes
+/// - Reads/Writes from checking/settings the on-chain storage version are accounted for
+pub type MigrateV0ToV1<T> = frame_support::migrations::VersionedMigration<
+	0, // The migration will only execute when the on-chain storage version is 0
+	1, // The on-chain storage version will be set to 1 after the migration is complete
+	InnerMigrateV0ToV1<T>,
+	crate::pallet::Pallet<T>,
+	<T as frame_system::Config>::DbWeight,
+>;
 
 /// Tests for our migration.
 ///
@@ -155,10 +129,10 @@ pub mod versioned {
 /// 3. The storage is in the expected state after the migration
 #[cfg(any(all(feature = "try-runtime", test), doc))]
 mod test {
+	use self::InnerMigrateV0ToV1;
 	use super::*;
 	use crate::mock::{new_test_ext, MockRuntime};
 	use frame_support::assert_ok;
-	use version_unchecked::MigrateV0ToV1;
 
 	#[test]
 	fn handles_no_existing_value() {
@@ -168,16 +142,16 @@ mod test {
 			assert!(v0::Value::<MockRuntime>::get().is_none());
 
 			// Get the pre_upgrade bytes
-			let bytes = match MigrateV0ToV1::<MockRuntime>::pre_upgrade() {
+			let bytes = match InnerMigrateV0ToV1::<MockRuntime>::pre_upgrade() {
 				Ok(bytes) => bytes,
 				Err(e) => panic!("pre_upgrade failed: {:?}", e),
 			};
 
 			// Execute the migration
-			let weight = MigrateV0ToV1::<MockRuntime>::on_runtime_upgrade();
+			let weight = InnerMigrateV0ToV1::<MockRuntime>::on_runtime_upgrade();
 
 			// Verify post_upgrade succeeds
-			assert_ok!(MigrateV0ToV1::<MockRuntime>::post_upgrade(bytes));
+			assert_ok!(InnerMigrateV0ToV1::<MockRuntime>::post_upgrade(bytes));
 
 			// The weight should be just 1 read for trying to access the old value.
 			assert_eq!(weight, <MockRuntime as frame_system::Config>::DbWeight::get().reads(1));
@@ -195,16 +169,16 @@ mod test {
 			v0::Value::<MockRuntime>::put(initial_value);
 
 			// Get the pre_upgrade bytes
-			let bytes = match MigrateV0ToV1::<MockRuntime>::pre_upgrade() {
+			let bytes = match InnerMigrateV0ToV1::<MockRuntime>::pre_upgrade() {
 				Ok(bytes) => bytes,
 				Err(e) => panic!("pre_upgrade failed: {:?}", e),
 			};
 
 			// Execute the migration
-			let weight = MigrateV0ToV1::<MockRuntime>::on_runtime_upgrade();
+			let weight = InnerMigrateV0ToV1::<MockRuntime>::on_runtime_upgrade();
 
 			// Verify post_upgrade succeeds
-			assert_ok!(MigrateV0ToV1::<MockRuntime>::post_upgrade(bytes));
+			assert_ok!(InnerMigrateV0ToV1::<MockRuntime>::post_upgrade(bytes));
 
 			// The weight used should be 1 read for the old value, and 1 write for the new
 			// value.
diff --git a/substrate/frame/grandpa/src/migrations/v5.rs b/substrate/frame/grandpa/src/migrations/v5.rs
index 24cfc34104b5f31cca70f93bc53309ee954a2d42..a0865a3f2bf9a8b6be340065b8b4658c97d5050a 100644
--- a/substrate/frame/grandpa/src/migrations/v5.rs
+++ b/substrate/frame/grandpa/src/migrations/v5.rs
@@ -20,7 +20,7 @@ use codec::Decode;
 use frame_support::{
 	migrations::VersionedMigration,
 	storage,
-	traits::{Get, OnRuntimeUpgrade},
+	traits::{Get, UncheckedOnRuntimeUpgrade},
 	weights::Weight,
 };
 use sp_consensus_grandpa::AuthorityList;
@@ -36,9 +36,9 @@ fn load_authority_list() -> AuthorityList {
 }
 
 /// Actual implementation of [`MigrateV4ToV5`].
-pub struct MigrateImpl<T>(PhantomData<T>);
+pub struct UncheckedMigrateImpl<T>(PhantomData<T>);
 
-impl<T: crate::Config> OnRuntimeUpgrade for MigrateImpl<T> {
+impl<T: crate::Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateImpl<T> {
 	#[cfg(feature = "try-runtime")]
 	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 		use codec::Encode;
@@ -92,5 +92,10 @@ impl<T: crate::Config> OnRuntimeUpgrade for MigrateImpl<T> {
 /// Migrate the storage from V4 to V5.
 ///
 /// Switches from `GRANDPA_AUTHORITIES_KEY` to a normal FRAME storage item.
-pub type MigrateV4ToV5<T> =
-	VersionedMigration<4, 5, MigrateImpl<T>, Pallet<T>, <T as frame_system::Config>::DbWeight>;
+pub type MigrateV4ToV5<T> = VersionedMigration<
+	4,
+	5,
+	UncheckedMigrateImpl<T>,
+	Pallet<T>,
+	<T as frame_system::Config>::DbWeight,
+>;
diff --git a/substrate/frame/identity/src/migration.rs b/substrate/frame/identity/src/migration.rs
index 88ac08d1bf5648e2640a2a248fb8d56113ef3c3a..8725bfd39df1409516c84d12e78b9d0f4bb03fc3 100644
--- a/substrate/frame/identity/src/migration.rs
+++ b/substrate/frame/identity/src/migration.rs
@@ -16,7 +16,9 @@
 //! Storage migrations for the Identity pallet.
 
 use super::*;
-use frame_support::{migrations::VersionedMigration, pallet_prelude::*, traits::OnRuntimeUpgrade};
+use frame_support::{
+	migrations::VersionedMigration, pallet_prelude::*, traits::UncheckedOnRuntimeUpgrade,
+};
 
 #[cfg(feature = "try-runtime")]
 use codec::{Decode, Encode};
@@ -66,7 +68,7 @@ pub mod v1 {
 	/// prevent stalling a parachain by accumulating too much weight in the migration. To have an
 	/// unlimited migration (e.g. in a chain without PoV limits), set this to `u64::MAX`.
 	pub struct VersionUncheckedMigrateV0ToV1<T, const KL: u64>(PhantomData<T>);
-	impl<T: Config, const KL: u64> OnRuntimeUpgrade for VersionUncheckedMigrateV0ToV1<T, KL> {
+	impl<T: Config, const KL: u64> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV0ToV1<T, KL> {
 		#[cfg(feature = "try-runtime")]
 		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
 			let identities = v0::IdentityOf::<T>::iter().count();
diff --git a/substrate/frame/nomination-pools/src/migration.rs b/substrate/frame/nomination-pools/src/migration.rs
index ca9c0874a83c45068b475358b6f3e9f5374b19b2..796b310862afcb8b897d4dafd66395e33e1970fb 100644
--- a/substrate/frame/nomination-pools/src/migration.rs
+++ b/substrate/frame/nomination-pools/src/migration.rs
@@ -17,7 +17,7 @@
 
 use super::*;
 use crate::log;
-use frame_support::traits::OnRuntimeUpgrade;
+use frame_support::traits::{OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade};
 use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
 
 #[cfg(feature = "try-runtime")]
@@ -132,7 +132,7 @@ pub mod v8 {
 	}
 
 	pub struct VersionUncheckedMigrateV7ToV8<T>(sp_std::marker::PhantomData<T>);
-	impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateV7ToV8<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV7ToV8<T> {
 		#[cfg(feature = "try-runtime")]
 		fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
 			Ok(Vec::new())
@@ -211,7 +211,7 @@ pub(crate) mod v7 {
 		CountedStorageMap<Pallet<T>, Twox64Concat, PoolId, V7BondedPoolInner<T>>;
 
 	pub struct VersionUncheckedMigrateV6ToV7<T>(sp_std::marker::PhantomData<T>);
-	impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateV6ToV7<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV6ToV7<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let migrated = BondedPools::<T>::count();
 			// The TVL should be the sum of all the funds that are actively staked and in the
@@ -282,7 +282,7 @@ mod v6 {
 			})
 		}
 	}
-	impl<T: Config> OnRuntimeUpgrade for MigrateToV6<T> {
+	impl<T: Config> UncheckedOnRuntimeUpgrade for MigrateToV6<T> {
 		fn on_runtime_upgrade() -> Weight {
 			let mut success = 0u64;
 			let mut fail = 0u64;
diff --git a/substrate/frame/society/src/migrations.rs b/substrate/frame/society/src/migrations.rs
index 8fd87b1163a4877c41c4b8ea30ace460e2916867..7ded1f84f5823696e922a3ef98e4cce45191d21c 100644
--- a/substrate/frame/society/src/migrations.rs
+++ b/substrate/frame/society/src/migrations.rs
@@ -19,7 +19,7 @@
 
 use super::*;
 use codec::{Decode, Encode};
-use frame_support::traits::{Defensive, DefensiveOption, Instance, OnRuntimeUpgrade};
+use frame_support::traits::{Defensive, DefensiveOption, Instance, UncheckedOnRuntimeUpgrade};
 
 #[cfg(feature = "try-runtime")]
 use sp_runtime::TryRuntimeError;
@@ -36,7 +36,7 @@ impl<
 		T: Config<I>,
 		I: Instance + 'static,
 		PastPayouts: Get<Vec<(<T as frame_system::Config>::AccountId, BalanceOf<T, I>)>>,
-	> OnRuntimeUpgrade for VersionUncheckedMigrateToV2<T, I, PastPayouts>
+	> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV2<T, I, PastPayouts>
 {
 	#[cfg(feature = "try-runtime")]
 	fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs
index 2ceab44cb16bd4bcf356caa07a3506aa76ba79db..b8cbcd69048147e7380357aa0b1adcdd4d03a0bc 100644
--- a/substrate/frame/support/src/migrations.rs
+++ b/substrate/frame/support/src/migrations.rs
@@ -41,19 +41,19 @@ use sp_std::{marker::PhantomData, vec::Vec};
 /// It takes 5 type parameters:
 /// - `From`: The version being upgraded from.
 /// - `To`: The version being upgraded to.
-/// - `Inner`: An implementation of `OnRuntimeUpgrade`.
+/// - `Inner`: An implementation of `UncheckedOnRuntimeUpgrade`.
 /// - `Pallet`: The Pallet being upgraded.
 /// - `Weight`: The runtime's RuntimeDbWeight implementation.
 ///
 /// When a [`VersionedMigration`] `on_runtime_upgrade`, `pre_upgrade`, or `post_upgrade` method is
 /// called, the on-chain version of the pallet is compared to `From`. If they match, the `Inner`
-/// equivalent is called and the pallets on-chain version is set to `To` after the migration.
-/// Otherwise, a warning is logged notifying the developer that the upgrade was a noop and should
-/// probably be removed.
+/// `UncheckedOnRuntimeUpgrade` is called and the pallets on-chain version is set to `To`
+/// after the migration. Otherwise, a warning is logged notifying the developer that the upgrade was
+/// a noop and should probably be removed.
 ///
-/// It is STRONGLY RECOMMENDED to write the unversioned migration logic in a private module and
-/// only export the versioned migration logic to prevent accidentally using the unversioned
-/// migration in any runtimes.
+/// By not bounding `Inner` with `OnRuntimeUpgrade`, we prevent developers from
+/// accidentally using the unchecked version of the migration in a runtime upgrade instead of
+/// [`VersionedMigration`].
 ///
 /// ### Examples
 /// ```ignore
@@ -71,9 +71,9 @@ use sp_std::{marker::PhantomData, vec::Vec};
 /// /// - https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504/40
 /// mod version_unchecked {
 /// 	use super::*;
-/// 	pub struct MigrateV5ToV6<T>(sp_std::marker::PhantomData<T>);
-/// 	impl<T: Config> OnRuntimeUpgrade for  VersionUncheckedMigrateV5ToV6<T> {
-/// 		// OnRuntimeUpgrade implementation...
+/// 	pub struct VersionUncheckedMigrateV5ToV6<T>(sp_std::marker::PhantomData<T>);
+/// 	impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6<T> {
+/// 		// `UncheckedOnRuntimeUpgrade` implementation...
 /// 	}
 /// }
 ///
@@ -116,7 +116,7 @@ pub enum VersionedPostUpgradeData {
 impl<
 		const FROM: u16,
 		const TO: u16,
-		Inner: crate::traits::OnRuntimeUpgrade,
+		Inner: crate::traits::UncheckedOnRuntimeUpgrade,
 		Pallet: GetStorageVersion<InCodeStorageVersion = StorageVersion> + PalletInfoAccess,
 		DbWeight: Get<RuntimeDbWeight>,
 	> crate::traits::OnRuntimeUpgrade for VersionedMigration<FROM, TO, Inner, Pallet, DbWeight>
diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index 24e7e1c8a65c219884b0e96b2483a89d79f32439..66777cef7b8e81960c1169c2fb1fd1033e58c4a2 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -87,7 +87,7 @@ pub use hooks::GenesisBuild;
 pub use hooks::{
 	BeforeAllRuntimeMigrations, BuildGenesisConfig, Hooks, IntegrityTest, OnFinalize, OnGenesis,
 	OnIdle, OnInitialize, OnPoll, OnRuntimeUpgrade, OnTimestampSet, PostInherents,
-	PostTransactions, PreInherents,
+	PostTransactions, PreInherents, UncheckedOnRuntimeUpgrade,
 };
 
 pub mod schedule;
diff --git a/substrate/frame/support/src/traits/hooks.rs b/substrate/frame/support/src/traits/hooks.rs
index d83e270474581ce2ca0aee93112a3b535a9271e3..ccccc5063286602538c0c46293ffabe8ad01181a 100644
--- a/substrate/frame/support/src/traits/hooks.rs
+++ b/substrate/frame/support/src/traits/hooks.rs
@@ -227,6 +227,30 @@ pub trait OnRuntimeUpgrade {
 	}
 }
 
+/// This trait is intended for use within `VersionedMigration` to execute storage migrations without
+/// automatic version checks. Implementations should ensure migration logic is safe and idempotent.
+pub trait UncheckedOnRuntimeUpgrade {
+	/// Called within `VersionedMigration` to execute the actual migration. It is also
+	/// expected that no version checks are performed within this function.
+	///
+	/// See also [`Hooks::on_runtime_upgrade`].
+	fn on_runtime_upgrade() -> Weight {
+		Weight::zero()
+	}
+
+	/// See [`Hooks::pre_upgrade`].
+	#[cfg(feature = "try-runtime")]
+	fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
+		Ok(Vec::new())
+	}
+
+	/// See [`Hooks::post_upgrade`].
+	#[cfg(feature = "try-runtime")]
+	fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
+		Ok(())
+	}
+}
+
 #[cfg_attr(all(not(feature = "tuples-96"), not(feature = "tuples-128")), impl_for_tuples(64))]
 #[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))]
 #[cfg_attr(feature = "tuples-128", impl_for_tuples(128))]
@@ -459,7 +483,9 @@ pub trait Hooks<BlockNumber> {
 	/// ## Implementation Note: Standalone Migrations
 	///
 	/// Additional migrations can be created by directly implementing [`OnRuntimeUpgrade`] on
-	/// structs and passing them to `Executive`.
+	/// structs and passing them to `Executive`. Or alternatively, by implementing
+	/// [`UncheckedOnRuntimeUpgrade`], passing it to [`crate::migrations::VersionedMigration`],
+	/// which already implements [`OnRuntimeUpgrade`].
 	///
 	/// ## Implementation Note: Pallet Versioning
 	///
diff --git a/substrate/frame/support/test/tests/versioned_migration.rs b/substrate/frame/support/test/tests/versioned_migration.rs
index 3fdfb902129ef8b16d82f7d6e4a16000ce0c61e3..e7d146940cb92f31020e8b27fe014983f50396f0 100644
--- a/substrate/frame/support/test/tests/versioned_migration.rs
+++ b/substrate/frame/support/test/tests/versioned_migration.rs
@@ -23,7 +23,7 @@ use frame_support::{
 	construct_runtime, derive_impl,
 	migrations::VersionedMigration,
 	parameter_types,
-	traits::{GetStorageVersion, OnRuntimeUpgrade, StorageVersion},
+	traits::{GetStorageVersion, OnRuntimeUpgrade, StorageVersion, UncheckedOnRuntimeUpgrade},
 	weights::constants::RocksDbWeight,
 };
 use frame_system::Config;
@@ -103,9 +103,11 @@ parameter_types! {
 	static PostUpgradeCalledWith: Vec<u8> = Vec::new();
 }
 
-/// Implement `OnRuntimeUpgrade` for `SomeUnversionedMigration`.
+/// Implement `UncheckedOnRuntimeUpgrade` for `SomeUnversionedMigration`.
 /// It sets SomeStorage to S, and returns a weight derived from UpgradeReads and UpgradeWrites.
-impl<T: dummy_pallet::Config, const S: u32> OnRuntimeUpgrade for SomeUnversionedMigration<T, S> {
+impl<T: dummy_pallet::Config, const S: u32> UncheckedOnRuntimeUpgrade
+	for SomeUnversionedMigration<T, S>
+{
 	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
 		PreUpgradeCalled::set(true);
 		Ok(PreUpgradeReturnBytes::get().to_vec())
diff --git a/substrate/frame/uniques/src/migration.rs b/substrate/frame/uniques/src/migration.rs
index ba0855a6bb657698d5c642181c3483459774a04f..90d44e7790d6d133bde9475851557cd625e559cb 100644
--- a/substrate/frame/uniques/src/migration.rs
+++ b/substrate/frame/uniques/src/migration.rs
@@ -18,15 +18,15 @@
 //! Various pieces of common functionality.
 use super::*;
 use core::marker::PhantomData;
-use frame_support::traits::{Get, OnRuntimeUpgrade};
+use frame_support::traits::{Get, UncheckedOnRuntimeUpgrade};
 
 mod v1 {
 	use super::*;
 
 	/// Actual implementation of the storage migration.
-	pub struct MigrateToV1Impl<T, I>(PhantomData<(T, I)>);
+	pub struct UncheckedMigrateToV1Impl<T, I>(PhantomData<(T, I)>);
 
-	impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for MigrateToV1Impl<T, I> {
+	impl<T: Config<I>, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrateToV1Impl<T, I> {
 		fn on_runtime_upgrade() -> frame_support::weights::Weight {
 			let mut count = 0;
 			for (collection, detail) in Collection::<T, I>::iter() {
@@ -49,7 +49,7 @@ mod v1 {
 pub type MigrateV0ToV1<T, I> = frame_support::migrations::VersionedMigration<
 	0,
 	1,
-	v1::MigrateToV1Impl<T, I>,
+	v1::UncheckedMigrateToV1Impl<T, I>,
 	Pallet<T, I>,
 	<T as frame_system::Config>::DbWeight,
 >;