diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6989fa435d125df6aaded215238f0fbd415217db..bc9f371db6267af7450780132382511cb52263f1 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -60,6 +60,25 @@
       "protocol": "inspector",
       "skipFiles": ["<node_internals>/**/*.js"]
     },
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Jest Current Folder",
+      "program": "${workspaceFolder}/node_modules/.bin/jest",
+      "args": [
+        "--runInBand",
+        "--collectCoverage=false",
+        "--testTimeout=100000000",
+        "--roots=${workspaceFolder}/${relativeFileDirname}"
+      ],
+      "console": "integratedTerminal",
+      "windows": {
+        "program": "${workspaceFolder}/node_modules/jest/bin/jest"
+      },
+      "runtimeArgs": ["--preserve-symlinks"],
+      "protocol": "inspector",
+      "skipFiles": ["<node_internals>/**/*.js"]
+    },
     {
       "type": "node",
       "name": "vscode-jest-tests",
diff --git a/jest.config.ts b/jest.config.ts
index eed6d79eca0472eb299556b797b827e73dff13bc..60490b835e4a8069a8649ba89bca1b74a024b511 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -30,6 +30,7 @@ const config: InitialOptionsTsJest = {
     'jest-extended/all',
     'expect-more-jest',
     '<rootDir>/test/setup.ts',
+    '<rootDir>/test/to-migrate.ts',
   ],
   snapshotSerializers: ['<rootDir>/test/newline-snapshot-serializer.ts'],
   testEnvironment: 'node',
diff --git a/lib/config/migration.ts b/lib/config/migration.ts
index 89fc7a49b9c2c71caa4a4aa36f33015d0b857371..b0af7ad93f3b01381a52458693a1b2897c6b5543 100644
--- a/lib/config/migration.ts
+++ b/lib/config/migration.ts
@@ -35,7 +35,7 @@ export function migrateConfig(
         optionTypes[option.name] = option.type;
       });
     }
-    const newConfig = MigrationsService.run(config).migratedConfig;
+    const newConfig = MigrationsService.run(config);
     const migratedConfig = clone(newConfig) as MigratedRenovateConfig;
     const depTypes = [
       'dependencies',
diff --git a/lib/config/migrations/base/abstract-migration.ts b/lib/config/migrations/base/abstract-migration.ts
index 9d47be887bd54ec967f78df9b3917f048557b358..258466e7f2e6225f4c3e6123cb177a82c26cd81a 100644
--- a/lib/config/migrations/base/abstract-migration.ts
+++ b/lib/config/migrations/base/abstract-migration.ts
@@ -3,10 +3,9 @@ import type { RenovateConfig } from '../../types';
 import type { Migration } from '../types';
 
 export abstract class AbstractMigration implements Migration {
+  readonly deprecated: boolean = false;
   abstract readonly propertyName: string;
-
   private readonly originalConfig: RenovateConfig;
-
   private readonly migratedConfig: RenovateConfig;
 
   constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
diff --git a/lib/config/migrations/base/rename-property-migration.ts b/lib/config/migrations/base/rename-property-migration.ts
index d883dd5069ee3ea9c959b86ecdbc55e0ea08b841..5a93a9c4dc392fa694c6f14eac23169a52391654 100644
--- a/lib/config/migrations/base/rename-property-migration.ts
+++ b/lib/config/migrations/base/rename-property-migration.ts
@@ -2,6 +2,7 @@ import type { RenovateConfig } from '../../types';
 import { AbstractMigration } from './abstract-migration';
 
 export class RenamePropertyMigration extends AbstractMigration {
+  override readonly deprecated = true;
   readonly propertyName: string;
 
   private readonly newPropertyName: string;
@@ -18,8 +19,6 @@ export class RenamePropertyMigration extends AbstractMigration {
   }
 
   override run(value): void {
-    this.delete(this.propertyName);
-
     this.setSafely(this.newPropertyName, value);
   }
 }
diff --git a/lib/config/migrations/custom/binary-source-migration.spec.ts b/lib/config/migrations/custom/binary-source-migration.spec.ts
index 5471ca20d0ae62623bbce2ba9a97d5cf127aa609..8e8ca4dd2b3f6b28be10f3122d9fc2fadc5c4cd2 100644
--- a/lib/config/migrations/custom/binary-source-migration.spec.ts
+++ b/lib/config/migrations/custom/binary-source-migration.spec.ts
@@ -1,14 +1,14 @@
-import { MigrationsService } from '../migrations-service';
+import { BinarySourceMigration } from './binary-source-migration';
 
 describe('config/migrations/custom/binary-source-migration', () => {
   it('should migrate "auto" to "global"', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      binarySource: 'auto',
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({
-      binarySource: 'global',
-    });
+    expect(BinarySourceMigration).toMigrate(
+      {
+        binarySource: 'auto',
+      },
+      {
+        binarySource: 'global',
+      }
+    );
   });
 });
diff --git a/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts b/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts
index 4f342710dcf25c398287f7972a29690bc52d8f06..747a7b5fe04ae6c36c6d039c625a93026722a514 100644
--- a/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts
+++ b/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts
@@ -1,35 +1,35 @@
-import { MigrationsService } from '../migrations-service';
+import { GoModTidyMigration } from './go-mod-tidy-migration';
 
 describe('config/migrations/custom/go-mod-tidy-migration', () => {
   it('should add postUpdateOptions option when true', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      gomodTidy: true,
-      postUpdateOptions: ['test'],
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({
-      postUpdateOptions: ['test', 'gomodTidy'],
-    });
+    expect(GoModTidyMigration).toMigrate(
+      {
+        gomodTidy: true,
+        postUpdateOptions: ['test'],
+      },
+      {
+        postUpdateOptions: ['test', 'gomodTidy'],
+      }
+    );
   });
 
   it('should handle case when postUpdateOptions is not defined ', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      gomodTidy: true,
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({
-      postUpdateOptions: ['gomodTidy'],
-    });
+    expect(GoModTidyMigration).toMigrate(
+      {
+        gomodTidy: true,
+      },
+      {
+        postUpdateOptions: ['gomodTidy'],
+      }
+    );
   });
 
   it('should only remove when false', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      gomodTidy: false,
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({});
+    expect(GoModTidyMigration).toMigrate(
+      {
+        gomodTidy: false,
+      },
+      {}
+    );
   });
 });
diff --git a/lib/config/migrations/custom/go-mod-tidy-migration.ts b/lib/config/migrations/custom/go-mod-tidy-migration.ts
index 9f3c984e363c864df4d24951bd24f07b9114eada..455a21bca41127750148b91f815029b128c05ea9 100644
--- a/lib/config/migrations/custom/go-mod-tidy-migration.ts
+++ b/lib/config/migrations/custom/go-mod-tidy-migration.ts
@@ -1,13 +1,12 @@
 import { AbstractMigration } from '../base/abstract-migration';
 
 export class GoModTidyMigration extends AbstractMigration {
+  override readonly deprecated = true;
   readonly propertyName = 'gomodTidy';
 
   override run(value): void {
     const postUpdateOptions = this.get('postUpdateOptions');
 
-    this.delete(this.propertyName);
-
     if (value) {
       const newPostUpdateOptions = Array.isArray(postUpdateOptions)
         ? postUpdateOptions.concat(['gomodTidy'])
diff --git a/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts b/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts
index 2be6ae247a70b6a88fe38f6c00e7a74e527af119..d90978b56f177eb797d0de5458dfc1527c13b94a 100644
--- a/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts
+++ b/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts
@@ -1,12 +1,12 @@
-import { MigrationsService } from '../migrations-service';
+import { IgnoreNodeModulesMigration } from './ignore-node-modules-migration';
 
 describe('config/migrations/custom/ignore-node-modules-migration', () => {
   it('should migrate to ignorePaths', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      ignoreNodeModules: true,
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({ ignorePaths: ['node_modules/'] });
+    expect(IgnoreNodeModulesMigration).toMigrate(
+      {
+        ignoreNodeModules: true,
+      },
+      { ignorePaths: ['node_modules/'] }
+    );
   });
 });
diff --git a/lib/config/migrations/custom/ignore-node-modules-migration.ts b/lib/config/migrations/custom/ignore-node-modules-migration.ts
index 79a1c77e7509402101d566842674c812c5edc06a..a579603f22f21e757d8c9b32808a0729a920344b 100644
--- a/lib/config/migrations/custom/ignore-node-modules-migration.ts
+++ b/lib/config/migrations/custom/ignore-node-modules-migration.ts
@@ -1,11 +1,10 @@
 import { AbstractMigration } from '../base/abstract-migration';
 
 export class IgnoreNodeModulesMigration extends AbstractMigration {
+  override readonly deprecated = true;
   readonly propertyName = 'ignoreNodeModules';
 
   override run(value): void {
-    this.delete(this.propertyName);
-
     this.setSafely('ignorePaths', value ? ['node_modules/'] : []);
   }
 }
diff --git a/lib/config/migrations/custom/required-status-checks-migration.spec.ts b/lib/config/migrations/custom/required-status-checks-migration.spec.ts
index 5aba3b883ceffd37849ade93106c9482a9aecba9..e664433ab3c5a202ceccbd8bea30ad6af7c550fc 100644
--- a/lib/config/migrations/custom/required-status-checks-migration.spec.ts
+++ b/lib/config/migrations/custom/required-status-checks-migration.spec.ts
@@ -1,14 +1,14 @@
-import { MigrationsService } from '../migrations-service';
+import { RequiredStatusChecksMigration } from './required-status-checks-migration';
 
 describe('config/migrations/custom/required-status-checks-migration', () => {
   it('should migrate requiredStatusChecks=null to ignoreTests=true', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      requiredStatusChecks: null,
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({
-      ignoreTests: true,
-    });
+    expect(RequiredStatusChecksMigration).toMigrate(
+      {
+        requiredStatusChecks: null,
+      },
+      {
+        ignoreTests: true,
+      }
+    );
   });
 });
diff --git a/lib/config/migrations/custom/required-status-checks-migration.ts b/lib/config/migrations/custom/required-status-checks-migration.ts
index a6fb5c303432e35afba5dacf68641fcb3698ff9f..db4bd86fb3c29024aac412103b4214f0fd7ccb6a 100644
--- a/lib/config/migrations/custom/required-status-checks-migration.ts
+++ b/lib/config/migrations/custom/required-status-checks-migration.ts
@@ -1,11 +1,10 @@
 import { AbstractMigration } from '../base/abstract-migration';
 
 export class RequiredStatusChecksMigration extends AbstractMigration {
+  override readonly deprecated = true;
   readonly propertyName = 'requiredStatusChecks';
 
   override run(value): void {
-    this.delete(this.propertyName);
-
     if (value === null) {
       this.setSafely('ignoreTests', true);
     }
diff --git a/lib/config/migrations/custom/trust-level-migration.spec.ts b/lib/config/migrations/custom/trust-level-migration.spec.ts
index 51ac5e070e7a0c17f08ea43553ce033d237bd42c..4ecb187dddc85c79bc31c08fc3af5b6547b41608 100644
--- a/lib/config/migrations/custom/trust-level-migration.spec.ts
+++ b/lib/config/migrations/custom/trust-level-migration.spec.ts
@@ -1,32 +1,32 @@
-import { MigrationsService } from '../migrations-service';
+import { TrustLevelMigration } from './trust-level-migration';
 
 describe('config/migrations/custom/trust-level-migration', () => {
   it('should handle hight level', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      trustLevel: 'high',
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({
-      allowCustomCrateRegistries: true,
-      allowScripts: true,
-      exposeAllEnv: true,
-    });
+    expect(TrustLevelMigration).toMigrate(
+      {
+        trustLevel: 'high',
+      },
+      {
+        allowCustomCrateRegistries: true,
+        allowScripts: true,
+        exposeAllEnv: true,
+      }
+    );
   });
 
   it('should not rewrite provided properties', () => {
-    const { isMigrated, migratedConfig } = MigrationsService.run({
-      allowCustomCrateRegistries: false,
-      allowScripts: false,
-      exposeAllEnv: false,
-      trustLevel: 'high',
-    });
-
-    expect(isMigrated).toBeTrue();
-    expect(migratedConfig).toEqual({
-      allowCustomCrateRegistries: false,
-      allowScripts: false,
-      exposeAllEnv: false,
-    });
+    expect(TrustLevelMigration).toMigrate(
+      {
+        allowCustomCrateRegistries: false,
+        allowScripts: false,
+        exposeAllEnv: false,
+        trustLevel: 'high',
+      },
+      {
+        allowCustomCrateRegistries: false,
+        allowScripts: false,
+        exposeAllEnv: false,
+      }
+    );
   });
 });
diff --git a/lib/config/migrations/custom/trust-level-migration.ts b/lib/config/migrations/custom/trust-level-migration.ts
index 95948940cf88d16d1090dfc78f98799d270661a7..b7ec2cab07e684aebe9010d42ea5dd8758e9cd33 100644
--- a/lib/config/migrations/custom/trust-level-migration.ts
+++ b/lib/config/migrations/custom/trust-level-migration.ts
@@ -1,11 +1,10 @@
 import { AbstractMigration } from '../base/abstract-migration';
 
 export class TrustLevelMigration extends AbstractMigration {
+  override readonly deprecated = true;
   readonly propertyName = 'trustLevel';
 
   override run(value): void {
-    this.delete(this.propertyName);
-
     if (value === 'high') {
       this.setSafely('allowCustomCrateRegistries', true);
       this.setSafely('allowScripts', true);
diff --git a/lib/config/migrations/migrations-service.spec.ts b/lib/config/migrations/migrations-service.spec.ts
index ae7ba803c0dbcc49cafd3c1f70565fec4b19e215..fdb47ac60d0c52384406dc1d129214a908c0a9c1 100644
--- a/lib/config/migrations/migrations-service.spec.ts
+++ b/lib/config/migrations/migrations-service.spec.ts
@@ -8,9 +8,10 @@ describe('config/migrations/migrations-service', () => {
         [property]: 'test',
       };
 
-      const { isMigrated, migratedConfig } =
-        MigrationsService.run(originalConfig);
-      expect(isMigrated).toBeTrue();
+      const migratedConfig = MigrationsService.run(originalConfig);
+      expect(
+        MigrationsService.isMigrated(originalConfig, migratedConfig)
+      ).toBeTrue();
       expect(migratedConfig).toEqual({});
     }
   });
@@ -24,9 +25,10 @@ describe('config/migrations/migrations-service', () => {
         [oldPropertyName]: 'test',
       };
 
-      const { isMigrated, migratedConfig } =
-        MigrationsService.run(originalConfig);
-      expect(isMigrated).toBeTrue();
+      const migratedConfig = MigrationsService.run(originalConfig);
+      expect(
+        MigrationsService.isMigrated(originalConfig, migratedConfig)
+      ).toBeTrue();
       expect(migratedConfig).toEqual({
         [newPropertyName]: 'test',
       });
@@ -39,14 +41,15 @@ describe('config/migrations/migrations-service', () => {
       versionScheme: 'test',
       excludedPackageNames: ['test'],
     };
-    const { isMigrated, migratedConfig } =
-      MigrationsService.run(originalConfig);
+    const migratedConfig = MigrationsService.run(originalConfig);
 
     const mappedProperties = Object.keys(originalConfig).map((property) =>
       MigrationsService.renamedProperties.get(property)
     );
 
-    expect(isMigrated).toBeTrue();
+    expect(
+      MigrationsService.isMigrated(originalConfig, migratedConfig)
+    ).toBeTrue();
     expect(mappedProperties).toEqual(Object.keys(migratedConfig));
   });
 });
diff --git a/lib/config/migrations/migrations-service.ts b/lib/config/migrations/migrations-service.ts
index a0d67ed42daea5a60ed5951e05888d801f34232e..f4abee1004966931822410e6c54f1b0753ff0e10 100644
--- a/lib/config/migrations/migrations-service.ts
+++ b/lib/config/migrations/migrations-service.ts
@@ -1,5 +1,5 @@
 import { dequal } from 'dequal';
-import type { MigratedConfig, RenovateConfig } from '../types';
+import type { RenovateConfig } from '../types';
 import { RemovePropertyMigration } from './base/remove-property-migration';
 import { RenamePropertyMigration } from './base/rename-property-migration';
 import { BinarySourceMigration } from './custom/binary-source-migration';
@@ -43,26 +43,34 @@ export class MigrationsService {
     TrustLevelMigration,
   ];
 
-  static run(originalConfig: RenovateConfig): MigratedConfig {
+  static run(originalConfig: RenovateConfig): RenovateConfig {
     const migratedConfig: RenovateConfig = {};
-    const migrations = MigrationsService.getMigrations(
-      originalConfig,
-      migratedConfig
-    );
+    const migrations = this.getMigrations(originalConfig, migratedConfig);
 
     for (const [key, value] of Object.entries(originalConfig)) {
       migratedConfig[key] ??= value;
       const migration = migrations.find((item) => item.propertyName === key);
-      migration?.run(value);
+
+      if (migration) {
+        migration.run(value);
+
+        if (migration.deprecated) {
+          delete migratedConfig[key];
+        }
+      }
     }
 
-    return {
-      isMigrated: !dequal(originalConfig, migratedConfig),
-      migratedConfig,
-    };
+    return migratedConfig;
+  }
+
+  static isMigrated(
+    originalConfig: RenovateConfig,
+    migratedConfig: RenovateConfig
+  ): boolean {
+    return !dequal(originalConfig, migratedConfig);
   }
 
-  private static getMigrations(
+  protected static getMigrations(
     originalConfig: RenovateConfig,
     migratedConfig: RenovateConfig
   ): ReadonlyArray<Migration> {
diff --git a/lib/config/migrations/types.ts b/lib/config/migrations/types.ts
index 206b294863df1e6deff3f0efd8decc456be15de7..429cbd069f65070e7dba799784a0fa15ec4dce73 100644
--- a/lib/config/migrations/types.ts
+++ b/lib/config/migrations/types.ts
@@ -7,6 +7,7 @@ export interface MigrationConstructor {
 }
 
 export interface Migration {
+  readonly deprecated: boolean;
   readonly propertyName: string;
   run(value: unknown): void;
 }
diff --git a/test/to-migrate.ts b/test/to-migrate.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38b7dea42d7c4bdff09c77b3145667f215e3fd14
--- /dev/null
+++ b/test/to-migrate.ts
@@ -0,0 +1,62 @@
+import { expect } from '@jest/globals';
+import type {
+  Migration,
+  MigrationConstructor,
+} from '../lib/config/migrations/types';
+import type { RenovateConfig } from '../lib/config/types';
+import { MigrationsService } from './../lib/config/migrations/migrations-service';
+
+declare global {
+  // eslint-disable-next-line @typescript-eslint/no-namespace
+  namespace jest {
+    interface Matchers<R> {
+      toMigrate(
+        originalConfig: RenovateConfig,
+        expectedConfig: RenovateConfig,
+        isMigrated?: boolean
+      ): R;
+    }
+  }
+}
+
+expect.extend({
+  toMigrate(
+    CustomMigration: MigrationConstructor,
+    originalConfig: RenovateConfig,
+    expectedConfig: RenovateConfig,
+    isMigrated = true
+  ) {
+    class CustomMigrationsService extends MigrationsService {
+      protected static override getMigrations(
+        original: RenovateConfig,
+        migrated: RenovateConfig
+      ): ReadonlyArray<Migration> {
+        return [new CustomMigration(original, migrated)];
+      }
+    }
+
+    const migratedConfig = CustomMigrationsService.run(originalConfig);
+
+    if (
+      MigrationsService.isMigrated(migratedConfig, originalConfig) !==
+      isMigrated
+    ) {
+      return {
+        message: (): string => `isMigrated should be ${isMigrated}`,
+        pass: false,
+      };
+    }
+
+    if (!this.equals(migratedConfig, expectedConfig)) {
+      return {
+        message: (): string => 'Migration failed',
+        pass: false,
+      };
+    }
+
+    return {
+      message: (): string => 'Migration passed successfully',
+      pass: true,
+    };
+  },
+});