diff --git a/lib/workers/repository/init/config.spec.ts b/lib/workers/repository/init/config.spec.ts
index 6ccdda752bf69f6caecd4e3c1897f83f6e063985..a28ca94ba861e8a771138af2105421b28891e5f0 100644
--- a/lib/workers/repository/init/config.spec.ts
+++ b/lib/workers/repository/init/config.spec.ts
@@ -6,6 +6,7 @@ import {
   mocked,
 } from '../../../../test/util';
 import * as _migrateAndValidate from '../../../config/migrate-validate';
+import * as _migrate from '../../../config/migration';
 import {
   checkForRepoConfigError,
   detectRepoFileConfig,
@@ -15,6 +16,7 @@ import {
 jest.mock('../../../util/fs');
 jest.mock('../../../util/git');
 
+const migrate = mocked(_migrate);
 const migrateAndValidate = mocked(_migrateAndValidate);
 
 let config: RenovateConfig;
@@ -25,6 +27,7 @@ beforeEach(() => {
   config.warnings = [];
 });
 
+jest.mock('../../../config/migration');
 jest.mock('../../../config/migrate-validate');
 
 describe('workers/repository/init/config', () => {
@@ -99,6 +102,12 @@ describe('workers/repository/init/config', () => {
     });
   });
   describe('mergeRenovateConfig()', () => {
+    beforeEach(() => {
+      migrate.migrateConfig.mockReturnValue({
+        isMigrated: false,
+        migratedConfig: {},
+      });
+    });
     it('throws error if misconfigured', async () => {
       git.getFileList.mockResolvedValue(['package.json', '.renovaterc.json']);
       fs.readLocalFile.mockResolvedValue('{}');
@@ -114,6 +123,20 @@ describe('workers/repository/init/config', () => {
       expect(e).toBeDefined();
       expect(e).toMatchSnapshot();
     });
+    it('migrates nested config', async () => {
+      git.getFileList.mockResolvedValue(['renovate.json']);
+      fs.readLocalFile.mockResolvedValue('{}');
+      migrateAndValidate.migrateAndValidate.mockResolvedValue({
+        warnings: [],
+        errors: [],
+      });
+      migrate.migrateConfig.mockReturnValueOnce({
+        isMigrated: true,
+        migratedConfig: {},
+      });
+      config.extends = [':automergeDisabled'];
+      expect(await mergeRenovateConfig(config)).not.toBeUndefined();
+    });
     it('continues if no errors', async () => {
       git.getFileList.mockResolvedValue(['package.json', '.renovaterc.json']);
       fs.readLocalFile.mockResolvedValue('{}');
diff --git a/lib/workers/repository/init/config.ts b/lib/workers/repository/init/config.ts
index 634c3a7f5a66b04c3712ceca838f3eb44d69fdf9..3c055fa863136b9852f3f706558c65de806c4917 100644
--- a/lib/workers/repository/init/config.ts
+++ b/lib/workers/repository/init/config.ts
@@ -7,6 +7,7 @@ import { RenovateConfig, mergeChildConfig } from '../../../config';
 import { configFileNames } from '../../../config/app-strings';
 import { decryptConfig } from '../../../config/decrypt';
 import { migrateAndValidate } from '../../../config/migrate-validate';
+import { migrateConfig } from '../../../config/migration';
 import * as presets from '../../../config/presets';
 import {
   CONFIG_VALIDATION,
@@ -186,10 +187,16 @@ export async function mergeRenovateConfig(
     npmApi.setNpmrc(decryptedConfig.npmrc);
   }
   // Decrypt after resolving in case the preset contains npm authentication instead
-  const resolvedConfig = decryptConfig(
+  let resolvedConfig = decryptConfig(
     await presets.resolveConfigPresets(decryptedConfig, config)
   );
   logger.trace({ config: resolvedConfig }, 'resolved config');
+  const migrationResult = migrateConfig(resolvedConfig);
+  if (migrationResult.isMigrated) {
+    logger.debug('Resolved config needs migrating');
+    logger.trace({ config: resolvedConfig }, 'resolved config after migrating');
+    resolvedConfig = migrationResult.migratedConfig;
+  }
   // istanbul ignore if
   if (is.string(resolvedConfig.npmrc)) {
     logger.debug(