diff --git a/lib/config/migration.ts b/lib/config/migration.ts
index fed7d7ffd750f6a992a0fdb08a9607d06c693e67..7699283c9242136c53960460228f6e9dfd9aad02 100644
--- a/lib/config/migration.ts
+++ b/lib/config/migration.ts
@@ -3,10 +3,8 @@ import { dequal } from 'dequal';
 import { logger } from '../logger';
 import { clone } from '../util/clone';
 import { regEx } from '../util/regex';
-import { GlobalConfig } from './global';
 import { MigrationsService } from './migrations';
 import { getOptions } from './options';
-import { removedPresets } from './presets/common';
 import type {
   MigratedConfig,
   MigratedRenovateConfig,
@@ -43,7 +41,6 @@ export function migrateConfig(
       'optionalDependencies',
       'peerDependencies',
     ];
-    const { migratePresets } = GlobalConfig.get();
     for (const [key, val] of Object.entries(newConfig)) {
       if (key === 'matchStrings' && is.array(val)) {
         migratedConfig.matchStrings = val
@@ -139,28 +136,6 @@ export function migrateConfig(
         } else {
           migratedConfig.semanticCommitScope = null;
         }
-      } else if (
-        key === 'extends' &&
-        (is.array<string>(val) || is.string(val))
-      ) {
-        if (is.string(migratedConfig.extends)) {
-          migratedConfig.extends = [migratedConfig.extends];
-        }
-        const presets = migratedConfig.extends;
-        for (let i = 0; i < presets.length; i += 1) {
-          const preset = presets[i];
-          if (is.string(preset)) {
-            let newPreset = removedPresets[preset];
-            if (newPreset !== undefined) {
-              presets[i] = newPreset;
-            }
-            newPreset = migratePresets?.[preset];
-            if (newPreset !== undefined) {
-              presets[i] = newPreset;
-            }
-          }
-        }
-        migratedConfig.extends = migratedConfig.extends.filter(Boolean);
       } else if (key === 'separateMajorReleases') {
         delete migratedConfig.separateMultipleMajor;
         migratedConfig.separateMajorMinor = val;
diff --git a/lib/config/migrations/custom/extends-migration.spec.ts b/lib/config/migrations/custom/extends-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..33dbe7faeda63a8219c2374a96d3c4f8c3f9f1ea
--- /dev/null
+++ b/lib/config/migrations/custom/extends-migration.spec.ts
@@ -0,0 +1,75 @@
+import { GlobalConfig } from '../../global';
+import { ExtendsMigration } from './extends-migration';
+
+describe('config/migrations/custom/extends-migration', () => {
+  it('it migrates preset strings to array', () => {
+    expect(ExtendsMigration).toMigrate(
+      {
+        extends: ':js-app',
+      } as any,
+      {
+        extends: ['config:js-app'],
+      }
+    );
+
+    expect(ExtendsMigration).toMigrate(
+      {
+        extends: 'foo',
+      } as any,
+      {
+        extends: ['foo'],
+      }
+    );
+  });
+
+  it('it migrates presets array', () => {
+    expect(ExtendsMigration).toMigrate(
+      {
+        extends: ['foo', ':js-app', 'bar'],
+      },
+      {
+        extends: ['foo', 'config:js-app', 'bar'],
+      }
+    );
+  });
+
+  it('should remove non string values', () => {
+    expect(ExtendsMigration).toMigrate(
+      {
+        extends: [{}],
+      } as any,
+      {
+        extends: [],
+      }
+    );
+  });
+
+  it('should remove removed presets', () => {
+    expect(ExtendsMigration).toMigrate(
+      {
+        extends: ['helpers:oddIsUnstable'],
+      },
+      {
+        extends: [],
+      }
+    );
+  });
+
+  it('it migrates presets', () => {
+    GlobalConfig.set({
+      migratePresets: {
+        '@org': 'local>org/renovate-config',
+        '@org2/foo': '',
+      },
+    });
+    expect(ExtendsMigration).toMigrate(
+      {
+        extends: ['@org', '@org2/foo'],
+      },
+      {
+        extends: ['local>org/renovate-config'],
+      }
+    );
+    GlobalConfig.reset();
+  });
+});
diff --git a/lib/config/migrations/custom/extends-migration.ts b/lib/config/migrations/custom/extends-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..64a444b9170ede0c92a77f8c2adbde5c6627197c
--- /dev/null
+++ b/lib/config/migrations/custom/extends-migration.ts
@@ -0,0 +1,44 @@
+import is from '@sindresorhus/is';
+import { GlobalConfig } from '../../global';
+import { removedPresets } from '../../presets/common';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class ExtendsMigration extends AbstractMigration {
+  override readonly propertyName = 'extends';
+
+  override run(): void {
+    const value = this.get('extends');
+    let newPresets: string[] = [];
+
+    if (is.string(value)) {
+      newPresets = this.normalizePresets([value]);
+    }
+
+    if (Array.isArray(value)) {
+      newPresets = this.normalizePresets(value);
+    }
+
+    this.rewrite(newPresets);
+  }
+
+  private normalizePresets(presets: string[]): string[] {
+    return presets
+      .filter(is.string)
+      .map((preset) => this.normalizePreset(preset))
+      .filter(is.nonEmptyString);
+  }
+
+  private normalizePreset(preset: string): string | null {
+    const { migratePresets } = GlobalConfig.get();
+
+    if (removedPresets[preset] !== undefined) {
+      return removedPresets[preset];
+    }
+
+    if (migratePresets?.[preset] !== undefined) {
+      return migratePresets?.[preset];
+    }
+
+    return preset;
+  }
+}
diff --git a/lib/config/migrations/migrations-service.ts b/lib/config/migrations/migrations-service.ts
index 6cb47c2d6a524682d0a413bebf231aab7dc309ee..ae065f7c4a6c691aec10632a7cf3b738894a54df 100644
--- a/lib/config/migrations/migrations-service.ts
+++ b/lib/config/migrations/migrations-service.ts
@@ -13,6 +13,7 @@ import { BranchNameMigration } from './custom/branch-name-migration';
 import { CompatibilityMigration } from './custom/compatibility-migration';
 import { ComposerIgnorePlatformReqsMigration } from './custom/composer-ignore-platform-reqs-migration';
 import { EnabledManagersMigration } from './custom/enabled-managers-migration';
+import { ExtendsMigration } from './custom/extends-migration';
 import { GoModTidyMigration } from './custom/go-mod-tidy-migration';
 import { HostRulesMigration } from './custom/host-rules-migration';
 import { IgnoreNodeModulesMigration } from './custom/ignore-node-modules-migration';
@@ -78,6 +79,7 @@ export class MigrationsService {
     CompatibilityMigration,
     ComposerIgnorePlatformReqsMigration,
     EnabledManagersMigration,
+    ExtendsMigration,
     GoModTidyMigration,
     HostRulesMigration,
     IgnoreNodeModulesMigration,