diff --git a/lib/workers/repository/init/config.spec.ts b/lib/workers/repository/init/config.spec.ts
deleted file mode 100644
index a119f7ff9898800a5679cfddd94ebd38b2bb184e..0000000000000000000000000000000000000000
--- a/lib/workers/repository/init/config.spec.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import type { AllConfig } from '../../../config/types';
-import { mergeStaticRepoEnvConfig } from './config';
-
-describe('workers/repository/init/config', () => {
-  describe('mergeRepoEnvConfig()', () => {
-    interface MergeRepoEnvTestCase {
-      name: string;
-      env: NodeJS.ProcessEnv;
-      currentConfig: AllConfig;
-      wantConfig: AllConfig;
-    }
-
-    const testCases: MergeRepoEnvTestCase[] = [
-      {
-        name: 'it does nothing',
-        env: {},
-        currentConfig: { repositories: ['some/repo'] },
-        wantConfig: { repositories: ['some/repo'] },
-      },
-      {
-        name: 'it merges env with the current config',
-        env: { RENOVATE_STATIC_REPO_CONFIG: '{"dependencyDashboard":true}' },
-        currentConfig: { repositories: ['some/repo'] },
-        wantConfig: {
-          dependencyDashboard: true,
-          repositories: ['some/repo'],
-        },
-      },
-      {
-        name: 'it ignores env with other renovate specific configuration options',
-        env: { RENOVATE_CONFIG: '{"dependencyDashboard":true}' },
-        currentConfig: { repositories: ['some/repo'] },
-        wantConfig: { repositories: ['some/repo'] },
-      },
-    ];
-
-    it.each(testCases)(
-      '$name',
-      async ({ env, currentConfig, wantConfig }: MergeRepoEnvTestCase) => {
-        const got = await mergeStaticRepoEnvConfig(currentConfig, env);
-
-        expect(got).toEqual(wantConfig);
-      },
-    );
-  });
-});
diff --git a/lib/workers/repository/init/config.ts b/lib/workers/repository/init/config.ts
index fe3122896b32aee3f819b32092c118f644e0800a..a8f9a14c6dfeceb8b5b3293cdccd72dce10ab32b 100644
--- a/lib/workers/repository/init/config.ts
+++ b/lib/workers/repository/init/config.ts
@@ -1,7 +1,4 @@
-import is from '@sindresorhus/is';
-import { mergeChildConfig } from '../../../config';
-import type { AllConfig, RenovateConfig } from '../../../config/types';
-import { parseAndValidateOrExit } from '../../global/config/parse/env';
+import type { RenovateConfig } from '../../../config/types';
 import { checkOnboardingBranch } from '../onboarding/branch';
 import { mergeInheritedConfig } from './inherited';
 import { mergeRenovateConfig } from './merge';
@@ -13,24 +10,7 @@ export async function getRepoConfig(
   let config = { ...config_ };
   config.baseBranch = config.defaultBranch;
   config = await mergeInheritedConfig(config);
-  config = await mergeStaticRepoEnvConfig(config, process.env);
   config = await checkOnboardingBranch(config);
   config = await mergeRenovateConfig(config);
   return config;
 }
-
-export async function mergeStaticRepoEnvConfig(
-  config: AllConfig,
-  env: NodeJS.ProcessEnv,
-): Promise<AllConfig> {
-  const repoEnvConfig = await parseAndValidateOrExit(
-    env,
-    'RENOVATE_STATIC_REPO_CONFIG',
-  );
-
-  if (!is.nonEmptyObject(repoEnvConfig)) {
-    return config;
-  }
-
-  return mergeChildConfig(config, repoEnvConfig);
-}
diff --git a/lib/workers/repository/init/merge.spec.ts b/lib/workers/repository/init/merge.spec.ts
index 00ee13353d132f9b7aa5e74442cdeef5ce79d40f..c975e3ee7d772c6a32827c8edf6c772e0ecc102b 100644
--- a/lib/workers/repository/init/merge.spec.ts
+++ b/lib/workers/repository/init/merge.spec.ts
@@ -11,6 +11,7 @@ import * as decrypt from '../../../config/decrypt';
 import { getConfig } from '../../../config/defaults';
 import * as _migrateAndValidate from '../../../config/migrate-validate';
 import * as _migrate from '../../../config/migration';
+import type { AllConfig } from '../../../config/types';
 import * as memCache from '../../../util/cache/memory';
 import * as repoCache from '../../../util/cache/repository';
 import { initRepoCache } from '../../../util/cache/repository/init';
@@ -21,6 +22,7 @@ import {
   checkForRepoConfigError,
   detectRepoFileConfig,
   mergeRenovateConfig,
+  mergeStaticRepoEnvConfig,
   setNpmTokenInNpmrc,
 } from './merge';
 
@@ -45,10 +47,13 @@ jest.mock('../../../config/migration');
 jest.mock('../../../config/migrate-validate');
 
 describe('workers/repository/init/merge', () => {
+  afterEach(() => {
+    jest.restoreAllMocks();
+  });
+
   describe('detectRepoFileConfig()', () => {
     beforeEach(async () => {
       await initRepoCache({ repoFingerprint: '0123456789abcdef' });
-      jest.restoreAllMocks();
     });
 
     it('returns config if not found', async () => {
@@ -469,4 +474,188 @@ describe('workers/repository/init/merge', () => {
       expect(config).toMatchObject({ npmrc: 'something_auth=token\n' });
     });
   });
+
+  describe('static repository config', () => {
+    const repoStaticConfigKey = 'RENOVATE_STATIC_REPO_CONFIG';
+
+    beforeEach(() => {
+      migrate.migrateConfig.mockImplementation((c) => ({
+        isMigrated: true,
+        migratedConfig: c,
+      }));
+      migrateAndValidate.migrateAndValidate.mockImplementationOnce((_, c) => {
+        return Promise.resolve({
+          ...c,
+          warnings: [],
+          errors: [],
+        });
+      });
+    });
+
+    describe('mergeStaticRepoEnvConfig()', () => {
+      interface MergeRepoEnvTestCase {
+        name: string;
+        currentConfig: AllConfig;
+        env: NodeJS.ProcessEnv;
+        want: AllConfig;
+      }
+
+      const testCases: MergeRepoEnvTestCase[] = [
+        {
+          name: 'it does nothing',
+          env: {},
+          currentConfig: { repositories: ['some/repo'] },
+          want: { repositories: ['some/repo'] },
+        },
+        {
+          name: 'it merges env with the current config',
+          env: { [repoStaticConfigKey]: '{"dependencyDashboard":true}' },
+          currentConfig: { repositories: ['some/repo'] },
+          want: {
+            dependencyDashboard: true,
+            repositories: ['some/repo'],
+          },
+        },
+        {
+          name: 'it ignores env with other renovate specific configuration options',
+          env: { RENOVATE_CONFIG: '{"dependencyDashboard":true}' },
+          currentConfig: { repositories: ['some/repo'] },
+          want: { repositories: ['some/repo'] },
+        },
+      ];
+
+      it.each(testCases)(
+        '$name',
+        async ({ env, currentConfig, want }: MergeRepoEnvTestCase) => {
+          const got = await mergeStaticRepoEnvConfig(currentConfig, env);
+
+          expect(got).toEqual(want);
+        },
+      );
+    });
+
+    describe('mergeRenovateConfig() with a static repository config', () => {
+      beforeEach(() => {
+        delete process.env[repoStaticConfigKey];
+
+        scm.getFileList.mockResolvedValueOnce(['renovate.json']);
+      });
+
+      interface MergeRepoFileAndEnvConfigTestCase {
+        name: string;
+        currentConfig: AllConfig;
+        repoFileConfig: AllConfig;
+        staticConfig: AllConfig;
+        wantConfig: AllConfig;
+      }
+
+      it.each<MergeRepoFileAndEnvConfigTestCase>([
+        {
+          name: 'it does nothing',
+          currentConfig: {},
+          repoFileConfig: {},
+          staticConfig: {},
+          wantConfig: {
+            renovateJsonPresent: true,
+            warnings: [],
+          },
+        },
+        {
+          name: 'it should resolve and use the repo file config when the static config is not set',
+          currentConfig: {},
+          repoFileConfig: {
+            extends: ['group:socketio'],
+          },
+          staticConfig: {},
+          wantConfig: {
+            description: ['Group socket.io packages.'],
+            packageRules: [
+              {
+                groupName: 'socket.io packages',
+                matchPackageNames: ['socket.io**'],
+              },
+            ],
+            renovateJsonPresent: true,
+            warnings: [],
+          },
+        },
+        {
+          name: 'it should resolve and use the static config when no repo file present',
+          currentConfig: {},
+          repoFileConfig: {},
+          staticConfig: { extends: ['group:socketio'] },
+          wantConfig: {
+            description: ['Group socket.io packages.'],
+            packageRules: [
+              {
+                groupName: 'socket.io packages',
+                matchPackageNames: ['socket.io**'],
+              },
+            ],
+            renovateJsonPresent: true,
+            warnings: [],
+          },
+        },
+        {
+          name: 'it should merge a static repo config into the repo config by appending it',
+          currentConfig: {},
+          repoFileConfig: {
+            extends: ['group:socketio'],
+            packageRules: [
+              {
+                matchConfidence: ['high', 'very high'],
+                groupName: 'high merge confidence',
+              },
+            ],
+          },
+          staticConfig: {
+            dependencyDashboard: true,
+            packageRules: [
+              {
+                groupName: 'my-custom-socketio-override',
+                matchPackageNames: ['socket.io**'],
+              },
+            ],
+          },
+          wantConfig: {
+            dependencyDashboard: true,
+            description: ['Group socket.io packages.'],
+            packageRules: [
+              {
+                groupName: 'socket.io packages',
+                matchPackageNames: ['socket.io**'],
+              },
+              {
+                groupName: 'high merge confidence',
+                matchConfidence: ['high', 'very high'],
+              },
+              {
+                groupName: 'my-custom-socketio-override',
+                matchPackageNames: ['socket.io**'],
+              },
+            ],
+            renovateJsonPresent: true,
+            warnings: [],
+          },
+        },
+      ])(
+        '$name',
+        async ({
+          staticConfig,
+          repoFileConfig,
+          currentConfig,
+          wantConfig,
+        }: MergeRepoFileAndEnvConfigTestCase) => {
+          fs.readLocalFile.mockResolvedValueOnce(
+            JSON.stringify(repoFileConfig),
+          );
+          process.env[repoStaticConfigKey] = JSON.stringify(staticConfig);
+
+          const got = await mergeRenovateConfig(currentConfig);
+
+          expect(got).toStrictEqual(wantConfig);
+        },
+      );
+    });
+  });
 });
diff --git a/lib/workers/repository/init/merge.ts b/lib/workers/repository/init/merge.ts
index 4cf53b1395c3e516da1afc083dd87af960fe4279..454b1939652bcb5699f19beb2fcb7533a0fdf464 100644
--- a/lib/workers/repository/init/merge.ts
+++ b/lib/workers/repository/init/merge.ts
@@ -7,7 +7,7 @@ import { migrateConfig } from '../../../config/migration';
 import { parseFileConfig } from '../../../config/parse';
 import * as presets from '../../../config/presets';
 import { applySecretsToConfig } from '../../../config/secrets';
-import type { RenovateConfig } from '../../../config/types';
+import type { AllConfig, RenovateConfig } from '../../../config/types';
 import {
   CONFIG_VALIDATION,
   REPOSITORY_CHANGED,
@@ -25,6 +25,7 @@ import * as queue from '../../../util/http/queue';
 import * as throttle from '../../../util/http/throttle';
 import { maskToken } from '../../../util/mask';
 import { regEx } from '../../../util/regex';
+import { parseAndValidateOrExit } from '../../global/config/parse/env';
 import { getOnboardingConfig } from '../onboarding/branch/config';
 import { getDefaultConfigFileName } from '../onboarding/branch/create';
 import {
@@ -187,15 +188,19 @@ export async function mergeRenovateConfig(
     };
   }
   const configFileParsed = repoConfig?.configFileParsed || {};
+  const configFileAndEnv = await mergeStaticRepoEnvConfig(
+    configFileParsed,
+    process.env,
+  );
   if (is.nonEmptyArray(returnConfig.extends)) {
-    configFileParsed.extends = [
+    configFileAndEnv.extends = [
       ...returnConfig.extends,
-      ...(configFileParsed.extends || []),
+      ...(configFileAndEnv.extends ?? []),
     ];
     delete returnConfig.extends;
   }
   checkForRepoConfigError(repoConfig);
-  const migratedConfig = await migrateAndValidate(config, configFileParsed);
+  const migratedConfig = await migrateAndValidate(config, configFileAndEnv);
   if (migratedConfig.errors?.length) {
     const error = new Error(CONFIG_VALIDATION);
     error.validationSource = repoConfig.configFileName;
@@ -312,3 +317,19 @@ export function setNpmTokenInNpmrc(config: RenovateConfig): void {
 
   delete config.npmToken;
 }
+
+export async function mergeStaticRepoEnvConfig(
+  config: AllConfig,
+  env: NodeJS.ProcessEnv,
+): Promise<AllConfig> {
+  const repoEnvConfig = await parseAndValidateOrExit(
+    env,
+    'RENOVATE_STATIC_REPO_CONFIG',
+  );
+
+  if (!is.nonEmptyObject(repoEnvConfig)) {
+    return config;
+  }
+
+  return mergeChildConfig(config, repoEnvConfig);
+}