diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md
index 03fb8d3490224e7281bfc3c0062096f085566e25..47b3c2a5b2544de31d10f87182fd44d166ed7e7a 100644
--- a/docs/usage/self-hosted-configuration.md
+++ b/docs/usage/self-hosted-configuration.md
@@ -519,6 +519,8 @@ Otherwise, Renovate skips onboarding a repository if it finds no dependencies in
 
 Similarly to `onboardingBranch`, if you have an existing Renovate installation and you change `onboardingPrTitle` then it's possible that you'll get onboarding PRs for repositories that had previously closed the onboarding PR unmerged.
 
+## onboardingRebaseCheckbox
+
 ## optimizeForDisabled
 
 When this option is `true`, Renovate will do the following during repository initialization:
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 1ef1e0ddd45aafd601eb8f48ee7e3c1a72734b1d..7d8c7aa990e1b978b567ae2b29e760d87e476c1a 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -399,6 +399,17 @@ const options: RenovateOptions[] = [
     globalOnly: true,
     mergeable: true,
   },
+  {
+    name: 'onboardingRebaseCheckbox',
+    description:
+      'Set to enable rebase/retry markdown checkbox for onboarding PRs.',
+    type: 'boolean',
+    default: false,
+    supportedPlatforms: ['github', 'gitlab', 'gitea'],
+    globalOnly: true,
+    experimental: true,
+    experimentalIssues: [17633],
+  },
   {
     name: 'includeForks',
     description:
diff --git a/lib/config/types.ts b/lib/config/types.ts
index d21dbe2b9cfc610bbab8486442cf55628857b5cd..1312571d410b79688b22d72497fc791fbf9237d8 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -142,6 +142,7 @@ export interface LegacyAdminConfig {
   onboardingBranch?: string;
   onboardingCommitMessage?: string;
   onboardingNoDeps?: boolean;
+  onboardingRebaseCheckbox?: boolean;
   onboardingPrTitle?: string;
   onboardingConfig?: RenovateSharedConfig;
   onboardingConfigFileName?: string;
diff --git a/lib/util/hasha.ts b/lib/util/hasha.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a9d9f22b3e3c45fb572c3f6b842df923d894723f
--- /dev/null
+++ b/lib/util/hasha.ts
@@ -0,0 +1,5 @@
+import hasha from 'hasha';
+
+export function toSha256(input: string): string {
+  return hasha(input, { algorithm: 'sha256' });
+}
diff --git a/lib/workers/repository/index.ts b/lib/workers/repository/index.ts
index 6489d08c09b689043144b8d2e84825f31e646150..0d6f4dd0e7af917facc97d4ffdc46f0a3d6777f2 100644
--- a/lib/workers/repository/index.ts
+++ b/lib/workers/repository/index.ts
@@ -19,8 +19,10 @@ import { ensureDependencyDashboard } from './dependency-dashboard';
 import handleError from './error';
 import { finaliseRepo } from './finalise';
 import { initRepo } from './init';
+import { OnboardingState } from './onboarding/common';
 import { ensureOnboardingPr } from './onboarding/pr';
 import { extractDependencies, updateRepo } from './process';
+import type { ExtractResult } from './process/extract-update';
 import { ProcessResult, processResult } from './result';
 import { printRequestStats } from './stats';
 
@@ -46,10 +48,13 @@ export async function renovateRepository(
     logger.debug('Using localDir: ' + localDir);
     config = await initRepo(config);
     addSplit('init');
-    const { branches, branchList, packageFiles } = await instrument(
-      'extract',
-      () => extractDependencies(config)
-    );
+    const performExtract =
+      config.repoIsOnboarded! ||
+      !config.onboardingRebaseCheckbox ||
+      OnboardingState.prUpdateRequested;
+    const { branches, branchList, packageFiles } = performExtract
+      ? await instrument('extract', () => extractDependencies(config))
+      : emptyExtract(config);
     if (config.semanticCommits === 'auto') {
       config.semanticCommits = await detectSemanticCommits();
     }
@@ -67,7 +72,9 @@ export async function renovateRepository(
       );
       setMeta({ repository: config.repository });
       addSplit('update');
-      await setBranchCache(branches);
+      if (performExtract) {
+        await setBranchCache(branches); // update branch cache if performed extraction
+      }
       if (res === 'automerged') {
         if (canRetry) {
           logger.info('Renovating repository again after automerge result');
@@ -109,3 +116,12 @@ export async function renovateRepository(
   logger.info({ cloned, durationMs: splits.total }, 'Repository finished');
   return repoResult;
 }
+
+// istanbul ignore next: renovateRepository is ignored
+function emptyExtract(config: RenovateConfig): ExtractResult {
+  return {
+    branches: [],
+    branchList: [config.onboardingBranch!], // to prevent auto closing
+    packageFiles: {},
+  };
+}
diff --git a/lib/workers/repository/onboarding/branch/index.spec.ts b/lib/workers/repository/onboarding/branch/index.spec.ts
index 14b20070a1e23b8bdb4b21cd43748686601f69c1..54f45225beea68ee14092ce97f03aa68a40f2007 100644
--- a/lib/workers/repository/onboarding/branch/index.spec.ts
+++ b/lib/workers/repository/onboarding/branch/index.spec.ts
@@ -8,14 +8,17 @@ import {
   platform,
 } from '../../../../../test/util';
 import { configFileNames } from '../../../../config/app-strings';
+import { GlobalConfig } from '../../../../config/global';
 import {
   REPOSITORY_FORKED,
   REPOSITORY_NO_PACKAGE_FILES,
 } from '../../../../constants/error-messages';
 import { logger } from '../../../../logger';
 import type { Pr } from '../../../../modules/platform';
+import * as memCache from '../../../../util/cache/memory';
 import * as _cache from '../../../../util/cache/repository';
 import type { FileAddition } from '../../../../util/git/types';
+import { OnboardingState } from '../common';
 import * as _config from './config';
 import * as _rebase from './rebase';
 import { checkOnboardingBranch } from '.';
@@ -36,9 +39,11 @@ describe('workers/repository/onboarding/branch/index', () => {
     let config: RenovateConfig;
 
     beforeEach(() => {
+      memCache.init();
       jest.resetAllMocks();
       config = getConfig();
       config.repository = 'some/repo';
+      OnboardingState.prUpdateRequested = false;
       git.getFileList.mockResolvedValue([]);
       cache.getCache.mockReturnValue({});
     });
@@ -63,26 +68,36 @@ describe('workers/repository/onboarding/branch/index', () => {
       );
     });
 
-    it('has default onboarding config', async () => {
-      configModule.getOnboardingConfig.mockResolvedValue(
-        config.onboardingConfig
-      );
-      configModule.getOnboardingConfigContents.mockResolvedValue(
-        '{\n' +
-          '  "$schema": "https://docs.renovatebot.com/renovate-schema.json"\n' +
-          '}\n'
-      );
-      git.getFileList.mockResolvedValue(['package.json']);
-      fs.readLocalFile.mockResolvedValue('{}');
-      await checkOnboardingBranch(config);
-      const file = git.commitFiles.mock.calls[0][0].files[0] as FileAddition;
-      const contents = file.contents?.toString();
-      expect(contents).toBeJsonString();
-      // TODO #7154
-      expect(JSON.parse(contents!)).toEqual({
-        $schema: 'https://docs.renovatebot.com/renovate-schema.json',
-      });
-    });
+    it.each`
+      checkboxEnabled | expected
+      ${true}         | ${true}
+      ${false}        | ${false}
+    `(
+      'has default onboarding config' +
+        '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+      async ({ checkboxEnabled, expected }) => {
+        config.onboardingRebaseCheckbox = checkboxEnabled;
+        configModule.getOnboardingConfig.mockResolvedValue(
+          config.onboardingConfig
+        );
+        configModule.getOnboardingConfigContents.mockResolvedValue(
+          '{\n' +
+            '  "$schema": "https://docs.renovatebot.com/renovate-schema.json"\n' +
+            '}\n'
+        );
+        git.getFileList.mockResolvedValue(['package.json']);
+        fs.readLocalFile.mockResolvedValue('{}');
+        await checkOnboardingBranch(config);
+        const file = git.commitFiles.mock.calls[0][0].files[0] as FileAddition;
+        const contents = file.contents?.toString();
+        expect(contents).toBeJsonString();
+        // TODO #7154
+        expect(JSON.parse(contents!)).toEqual({
+          $schema: 'https://docs.renovatebot.com/renovate-schema.json',
+        });
+        expect(OnboardingState.prUpdateRequested).toBe(expected);
+      }
+    );
 
     it('uses discovered onboarding config', async () => {
       configModule.getOnboardingConfig.mockResolvedValue({
@@ -244,5 +259,71 @@ describe('workers/repository/onboarding/branch/index', () => {
       expect(git.checkoutBranch).toHaveBeenCalledTimes(1);
       expect(git.commitFiles).toHaveBeenCalledTimes(0);
     });
+
+    describe('tests onboarding rebase/retry checkbox handling', () => {
+      beforeEach(() => {
+        GlobalConfig.set({ platform: 'github' });
+        config.onboardingRebaseCheckbox = true;
+        OnboardingState.prUpdateRequested = false;
+        git.getFileList.mockResolvedValueOnce(['package.json']);
+        platform.findPr.mockResolvedValueOnce(null);
+        rebase.rebaseOnboardingBranch.mockResolvedValueOnce(null);
+      });
+
+      it('detects unsupported platfom', async () => {
+        const pl = 'bitbucket';
+        GlobalConfig.set({ platform: pl });
+        platform.getBranchPr.mockResolvedValueOnce(mock<Pr>({}));
+
+        await checkOnboardingBranch(config);
+
+        expect(logger.trace).toHaveBeenCalledWith(
+          `Platform '${pl}' does not support extended markdown`
+        );
+        expect(OnboardingState.prUpdateRequested).toBeTrue();
+        expect(git.checkoutBranch).toHaveBeenCalledTimes(1);
+        expect(git.commitFiles).toHaveBeenCalledTimes(0);
+      });
+
+      it('detects missing rebase checkbox', async () => {
+        const pr = { bodyStruct: { rebaseRequested: undefined } };
+        platform.getBranchPr.mockResolvedValueOnce(mock<Pr>(pr));
+
+        await checkOnboardingBranch(config);
+
+        expect(logger.debug).toHaveBeenCalledWith(
+          `No rebase checkbox was found in the onboarding PR`
+        );
+        expect(OnboardingState.prUpdateRequested).toBeTrue();
+        expect(git.checkoutBranch).toHaveBeenCalledTimes(1);
+        expect(git.commitFiles).toHaveBeenCalledTimes(0);
+      });
+
+      it('detects manual pr update requested', async () => {
+        const pr = { bodyStruct: { rebaseRequested: true } };
+        platform.getBranchPr.mockResolvedValueOnce(mock<Pr>(pr));
+
+        await checkOnboardingBranch(config);
+
+        expect(logger.debug).toHaveBeenCalledWith(
+          `Manual onboarding PR update requested`
+        );
+        expect(OnboardingState.prUpdateRequested).toBeTrue();
+        ``;
+        expect(git.checkoutBranch).toHaveBeenCalledTimes(1);
+        expect(git.commitFiles).toHaveBeenCalledTimes(0);
+      });
+
+      it('handles unchecked rebase checkbox', async () => {
+        const pr = { bodyStruct: { rebaseRequested: false } };
+        platform.getBranchPr.mockResolvedValueOnce(mock<Pr>(pr));
+
+        await checkOnboardingBranch(config);
+
+        expect(OnboardingState.prUpdateRequested).toBeFalse();
+        expect(git.checkoutBranch).toHaveBeenCalledTimes(1);
+        expect(git.commitFiles).toHaveBeenCalledTimes(0);
+      });
+    });
   });
 });
diff --git a/lib/workers/repository/onboarding/branch/index.ts b/lib/workers/repository/onboarding/branch/index.ts
index 3707f16426f5263b68db5fb7b9d00115525c594d..0489d7e1c7adce0ecc4d103e45637e2de1a85a1e 100644
--- a/lib/workers/repository/onboarding/branch/index.ts
+++ b/lib/workers/repository/onboarding/branch/index.ts
@@ -1,3 +1,4 @@
+import is from '@sindresorhus/is';
 import { mergeChildConfig } from '../../../../config';
 import { GlobalConfig } from '../../../../config/global';
 import type { RenovateConfig } from '../../../../config/types';
@@ -6,10 +7,11 @@ import {
   REPOSITORY_NO_PACKAGE_FILES,
 } from '../../../../constants/error-messages';
 import { logger } from '../../../../logger';
-import { platform } from '../../../../modules/platform';
+import { Pr, platform } from '../../../../modules/platform';
 import { checkoutBranch, setGitAuthor } from '../../../../util/git';
 import { extractAllDependencies } from '../../extract';
 import { mergeRenovateConfig } from '../../init/merge';
+import { OnboardingState } from '../common';
 import { getOnboardingPr, isOnboarded } from './check';
 import { getOnboardingConfig } from './config';
 import { createOnboardingBranch } from './create';
@@ -34,8 +36,12 @@ export async function checkOnboardingBranch(
   setGitAuthor(config.gitAuthor);
   const onboardingPr = await getOnboardingPr(config);
   if (onboardingPr) {
+    if (config.onboardingRebaseCheckbox) {
+      handleOnboardingManualRebase(onboardingPr);
+    }
     logger.debug('Onboarding PR already exists');
-    const commit = await rebaseOnboardingBranch(config);
+    const { rawConfigHash } = onboardingPr.bodyStruct ?? {};
+    const commit = await rebaseOnboardingBranch(config, rawConfigHash);
     if (commit) {
       logger.info(
         { branch: config.onboardingBranch, commit, onboarding: true },
@@ -44,7 +50,6 @@ export async function checkOnboardingBranch(
     }
     // istanbul ignore if
     if (platform.refreshPr) {
-      // TODO #7154
       await platform.refreshPr(onboardingPr.number);
     }
   } else {
@@ -62,6 +67,9 @@ export async function checkOnboardingBranch(
       }
     }
     logger.debug('Need to create onboarding PR');
+    if (config.onboardingRebaseCheckbox) {
+      OnboardingState.prUpdateRequested = true;
+    }
     const commit = await createOnboardingBranch(mergedConfig);
     // istanbul ignore if
     if (commit) {
@@ -80,3 +88,18 @@ export async function checkOnboardingBranch(
   const branchList = [onboardingBranch!];
   return { ...config, repoIsOnboarded, onboardingBranch, branchList };
 }
+
+function handleOnboardingManualRebase(onboardingPr: Pr): void {
+  const pl = GlobalConfig.get('platform')!;
+  const { rebaseRequested } = onboardingPr.bodyStruct ?? {};
+  if (!['github', 'gitlab', 'gitea'].includes(pl)) {
+    logger.trace(`Platform '${pl}' does not support extended markdown`);
+    OnboardingState.prUpdateRequested = true;
+  } else if (is.nullOrUndefined(rebaseRequested)) {
+    logger.debug('No rebase checkbox was found in the onboarding PR');
+    OnboardingState.prUpdateRequested = true;
+  } else if (rebaseRequested) {
+    logger.debug('Manual onboarding PR update requested');
+    OnboardingState.prUpdateRequested = true;
+  }
+}
diff --git a/lib/workers/repository/onboarding/branch/rebase.spec.ts b/lib/workers/repository/onboarding/branch/rebase.spec.ts
index eeeaf88e11b70ba913aef39c9a3295a07daaf13e..be1f80cf80cdb1020c68783b62fd34523d3ab0c4 100644
--- a/lib/workers/repository/onboarding/branch/rebase.spec.ts
+++ b/lib/workers/repository/onboarding/branch/rebase.spec.ts
@@ -5,6 +5,9 @@ import {
   platform,
 } from '../../../../../test/util';
 import { GlobalConfig } from '../../../../config/global';
+import * as memCache from '../../../../util/cache/memory';
+import { toSha256 } from '../../../../util/hasha';
+import { OnboardingState } from '../common';
 import { rebaseOnboardingBranch } from './rebase';
 
 jest.mock('../../../../util/git');
@@ -18,9 +21,12 @@ describe('workers/repository/onboarding/branch/rebase', () => {
 
   describe('rebaseOnboardingBranch()', () => {
     let config: RenovateConfig;
+    const hash = '';
 
     beforeEach(() => {
+      memCache.init();
       jest.resetAllMocks();
+      OnboardingState.prUpdateRequested = false;
       config = {
         ...getConfig(),
         repository: 'some/repo',
@@ -29,61 +35,168 @@ describe('workers/repository/onboarding/branch/rebase', () => {
 
     it('does not rebase modified branch', async () => {
       git.isBranchModified.mockResolvedValueOnce(true);
-      await rebaseOnboardingBranch(config);
+      await rebaseOnboardingBranch(config, hash);
       expect(git.commitFiles).toHaveBeenCalledTimes(0);
     });
 
-    it('does nothing if branch is up to date', async () => {
+    it.each`
+      checkboxEnabled
+      ${true}
+      ${false}
+    `(
+      'does nothing if branch is up to date ' +
+        '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+      async ({ checkboxEnabled }) => {
+        config.onboardingRebaseCheckbox = checkboxEnabled;
+        const contents =
+          JSON.stringify(getConfig().onboardingConfig, null, 2) + '\n';
+        git.getFile
+          .mockResolvedValueOnce(contents) // package.json
+          .mockResolvedValueOnce(contents); // renovate.json
+        await rebaseOnboardingBranch(config, toSha256(contents));
+        expect(git.commitFiles).toHaveBeenCalledTimes(0);
+        expect(OnboardingState.prUpdateRequested).toBeFalse();
+      }
+    );
+
+    it.each`
+      checkboxEnabled | expected
+      ${true}         | ${true}
+      ${false}        | ${false}
+    `(
+      'rebases onboarding branch ' +
+        '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+      async ({ checkboxEnabled, expected }) => {
+        config.onboardingRebaseCheckbox = checkboxEnabled;
+        git.isBranchBehindBase.mockResolvedValueOnce(true);
+        await rebaseOnboardingBranch(config, hash);
+        expect(git.commitFiles).toHaveBeenCalledTimes(1);
+        expect(OnboardingState.prUpdateRequested).toBe(expected);
+      }
+    );
+
+    it.each`
+      checkboxEnabled | expected
+      ${true}         | ${true}
+      ${false}        | ${false}
+    `(
+      'rebases via platform ' +
+        '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+      async ({ checkboxEnabled, expected }) => {
+        platform.commitFiles = jest.fn();
+        config.platformCommit = true;
+        config.onboardingRebaseCheckbox = checkboxEnabled;
+        git.isBranchBehindBase.mockResolvedValueOnce(true);
+        await rebaseOnboardingBranch(config, hash);
+        expect(platform.commitFiles).toHaveBeenCalledTimes(1);
+        expect(OnboardingState.prUpdateRequested).toBe(expected);
+      }
+    );
+
+    it.each`
+      checkboxEnabled | expected
+      ${true}         | ${true}
+      ${false}        | ${false}
+    `(
+      'uses the onboardingConfigFileName if set ' +
+        '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+      async ({ checkboxEnabled, expected }) => {
+        git.isBranchBehindBase.mockResolvedValueOnce(true);
+        await rebaseOnboardingBranch({
+          ...config,
+          onboardingConfigFileName: '.github/renovate.json',
+          onboardingRebaseCheckbox: checkboxEnabled,
+        });
+        expect(git.commitFiles).toHaveBeenCalledTimes(1);
+        expect(git.commitFiles.mock.calls[0][0].message).toContain(
+          '.github/renovate.json'
+        );
+        expect(git.commitFiles.mock.calls[0][0].files[0].path).toBe(
+          '.github/renovate.json'
+        );
+        expect(OnboardingState.prUpdateRequested).toBe(expected);
+      }
+    );
+
+    it.each`
+      checkboxEnabled | expected
+      ${true}         | ${true}
+      ${false}        | ${false}
+    `(
+      'falls back to "renovate.json" if onboardingConfigFileName is not set ' +
+        '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+      async ({ checkboxEnabled, expected }) => {
+        git.isBranchBehindBase.mockResolvedValueOnce(true);
+        await rebaseOnboardingBranch({
+          ...config,
+          onboardingConfigFileName: undefined,
+          onboardingRebaseCheckbox: checkboxEnabled,
+        });
+        expect(git.commitFiles).toHaveBeenCalledTimes(1);
+        expect(git.commitFiles.mock.calls[0][0].message).toContain(
+          'renovate.json'
+        );
+        expect(git.commitFiles.mock.calls[0][0].files[0].path).toBe(
+          'renovate.json'
+        );
+        expect(OnboardingState.prUpdateRequested).toBe(expected);
+      }
+    );
+
+    describe('handle onboarding config hashes', () => {
       const contents =
         JSON.stringify(getConfig().onboardingConfig, null, 2) + '\n';
-      git.getFile
-        .mockResolvedValueOnce(contents) // package.json
-        .mockResolvedValueOnce(contents); // renovate.json
-      await rebaseOnboardingBranch(config);
-      expect(git.commitFiles).toHaveBeenCalledTimes(0);
-    });
 
-    it('rebases onboarding branch', async () => {
-      git.isBranchBehindBase.mockResolvedValueOnce(true);
-      await rebaseOnboardingBranch(config);
-      expect(git.commitFiles).toHaveBeenCalledTimes(1);
-    });
+      beforeEach(() => {
+        git.isBranchModified.mockResolvedValueOnce(true);
+        git.getFile.mockResolvedValueOnce(contents);
+      });
 
-    it('rebases via platform', async () => {
-      platform.commitFiles = jest.fn();
-      config.platformCommit = true;
-      git.isBranchBehindBase.mockResolvedValueOnce(true);
-      await rebaseOnboardingBranch(config);
-      expect(platform.commitFiles).toHaveBeenCalledTimes(1);
-    });
+      it.each`
+        checkboxEnabled | expected
+        ${true}         | ${true}
+        ${false}        | ${false}
+      `(
+        'handles a missing previous config hash ' +
+          '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+        async ({ checkboxEnabled, expected }) => {
+          config.onboardingRebaseCheckbox = checkboxEnabled;
+          await rebaseOnboardingBranch(config, undefined);
 
-    it('uses the onboardingConfigFileName if set', async () => {
-      git.isBranchBehindBase.mockResolvedValueOnce(true);
-      await rebaseOnboardingBranch({
-        ...config,
-        onboardingConfigFileName: '.github/renovate.json',
-      });
-      expect(git.commitFiles).toHaveBeenCalledTimes(1);
-      expect(git.commitFiles.mock.calls[0][0].message).toContain(
-        '.github/renovate.json'
-      );
-      expect(git.commitFiles.mock.calls[0][0].files[0].path).toBe(
-        '.github/renovate.json'
+          expect(OnboardingState.prUpdateRequested).toBe(expected);
+        }
       );
-    });
 
-    it('falls back to "renovate.json" if onboardingConfigFileName is not set', async () => {
-      git.isBranchBehindBase.mockResolvedValueOnce(true);
-      await rebaseOnboardingBranch({
-        ...config,
-        onboardingConfigFileName: undefined,
-      });
-      expect(git.commitFiles).toHaveBeenCalledTimes(1);
-      expect(git.commitFiles.mock.calls[0][0].message).toContain(
-        'renovate.json'
+      it.each`
+        checkboxEnabled
+        ${true}
+        ${false}
+      `(
+        'does nothing if config hashes match' +
+          '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+        async ({ checkboxEnabled, expected }) => {
+          git.getFile.mockResolvedValueOnce(contents); // package.json
+          config.onboardingRebaseCheckbox = checkboxEnabled;
+          await rebaseOnboardingBranch(config, toSha256(contents));
+          expect(git.commitFiles).toHaveBeenCalledTimes(0);
+          expect(OnboardingState.prUpdateRequested).toBeFalse();
+        }
       );
-      expect(git.commitFiles.mock.calls[0][0].files[0].path).toBe(
-        'renovate.json'
+
+      it.each`
+        checkboxEnabled | expected
+        ${true}         | ${true}
+        ${false}        | ${false}
+      `(
+        'requests update if config hashes mismatch' +
+          '(config.onboardingRebaseCheckbox="$checkboxEnabled")',
+        async ({ checkboxEnabled, expected }) => {
+          git.getFile.mockResolvedValueOnce(contents); // package.json
+          config.onboardingRebaseCheckbox = checkboxEnabled;
+          await rebaseOnboardingBranch(config, hash);
+          expect(git.commitFiles).toHaveBeenCalledTimes(0);
+          expect(OnboardingState.prUpdateRequested).toBe(expected);
+        }
       );
     });
   });
diff --git a/lib/workers/repository/onboarding/branch/rebase.ts b/lib/workers/repository/onboarding/branch/rebase.ts
index 94ca8f1798c93b073fe146fc41faaf5939d22b21..2de5ce5438777461e6729585b0e7d6e64918a357 100644
--- a/lib/workers/repository/onboarding/branch/rebase.ts
+++ b/lib/workers/repository/onboarding/branch/rebase.ts
@@ -1,4 +1,4 @@
-import { configFileNames } from '../../../../config/app-strings';
+import is from '@sindresorhus/is';
 import { GlobalConfig } from '../../../../config/global';
 import type { RenovateConfig } from '../../../../config/types';
 import { logger } from '../../../../logger';
@@ -8,27 +8,32 @@ import {
   isBranchBehindBase,
   isBranchModified,
 } from '../../../../util/git';
+import { toSha256 } from '../../../../util/hasha';
+import { OnboardingState, defaultConfigFile } from '../common';
 import { OnboardingCommitMessageFactory } from './commit-message';
 import { getOnboardingConfigContents } from './config';
 
-function defaultConfigFile(config: RenovateConfig): string {
-  return configFileNames.includes(config.onboardingConfigFileName!)
-    ? config.onboardingConfigFileName!
-    : configFileNames[0];
-}
-
 export async function rebaseOnboardingBranch(
-  config: RenovateConfig
+  config: RenovateConfig,
+  previousConfigHash?: string
 ): Promise<string | null> {
   logger.debug('Checking if onboarding branch needs rebasing');
+  const configFile = defaultConfigFile(config);
+  const existingContents =
+    (await getFile(configFile, config.onboardingBranch)) ?? '';
+  const currentConfigHash = toSha256(existingContents);
+  const contents = await getOnboardingConfigContents(config, configFile);
+
+  if (config.onboardingRebaseCheckbox) {
+    handleOnboardingManualRebase(previousConfigHash, currentConfigHash);
+  }
+
   // TODO #7154
   if (await isBranchModified(config.onboardingBranch!)) {
     logger.debug('Onboarding branch has been edited and cannot be rebased');
     return null;
   }
-  const configFile = defaultConfigFile(config);
-  const existingContents = await getFile(configFile, config.onboardingBranch);
-  const contents = await getOnboardingConfigContents(config, configFile);
+
   // TODO: fix types (#7154)
   if (
     contents === existingContents &&
@@ -37,7 +42,11 @@ export async function rebaseOnboardingBranch(
     logger.debug('Onboarding branch is up to date');
     return null;
   }
+
   logger.debug('Rebasing onboarding branch');
+  if (config.onboardingRebaseCheckbox) {
+    OnboardingState.prUpdateRequested = true;
+  }
   // istanbul ignore next
   const commitMessageFactory = new OnboardingCommitMessageFactory(
     config,
@@ -65,3 +74,16 @@ export async function rebaseOnboardingBranch(
     platformCommit: !!config.platformCommit,
   });
 }
+
+function handleOnboardingManualRebase(
+  previousConfigHash: string | undefined,
+  currentConfigHash: string
+): void {
+  if (is.nullOrUndefined(previousConfigHash)) {
+    logger.debug('Missing previousConfigHash bodyStruct prop in onboarding PR');
+    OnboardingState.prUpdateRequested = true;
+  } else if (previousConfigHash !== currentConfigHash) {
+    logger.debug('Onboarding config has been modified by the user');
+    OnboardingState.prUpdateRequested = true;
+  }
+}
diff --git a/lib/workers/repository/onboarding/common.ts b/lib/workers/repository/onboarding/common.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5738c3d09d33afa835b241c6c714f56cdb4d7d8e
--- /dev/null
+++ b/lib/workers/repository/onboarding/common.ts
@@ -0,0 +1,30 @@
+import { configFileNames } from '../../../config/app-strings';
+import type { RenovateConfig } from '../../../config/types';
+import { logger } from '../../../logger';
+import * as memCache from '../../../util/cache/memory';
+
+export function defaultConfigFile(config: RenovateConfig): string {
+  return configFileNames.includes(config.onboardingConfigFileName!)
+    ? config.onboardingConfigFileName!
+    : configFileNames[0];
+}
+
+export class OnboardingState {
+  private static readonly cacheKey = 'OnboardingState';
+
+  static get prUpdateRequested(): boolean {
+    const updateRequested = !!memCache.get<boolean | undefined>(
+      OnboardingState.cacheKey
+    );
+    logger.trace(
+      { value: updateRequested },
+      'Get OnboardingState.prUpdateRequested'
+    );
+    return updateRequested;
+  }
+
+  static set prUpdateRequested(value: boolean) {
+    logger.trace({ value }, 'Set OnboardingState.prUpdateRequested');
+    memCache.set(OnboardingState.cacheKey, value);
+  }
+}
diff --git a/lib/workers/repository/onboarding/pr/__snapshots__/index.spec.ts.snap b/lib/workers/repository/onboarding/pr/__snapshots__/index.spec.ts.snap
index 59b7ac89742b7d102a2a303d20947fd7d0a03962..f8041b7fcb64ddf6c26fdce541f1e397c67cac2a 100644
--- a/lib/workers/repository/onboarding/pr/__snapshots__/index.spec.ts.snap
+++ b/lib/workers/repository/onboarding/pr/__snapshots__/index.spec.ts.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with empty footer and header 1`] = `
+exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with empty footer and header(onboardingRebaseCheckbox="false") 1`] = `
 "
 
 Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.
@@ -30,7 +30,44 @@ If you need any further assistance then you can also [request help here](https:/
 "
 `;
 
-exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with footer and header using templating 1`] = `
+exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with empty footer and header(onboardingRebaseCheckbox="true") 1`] = `
+"
+
+Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.
+
+🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged.
+
+
+
+---
+### Detected Package Files
+
+ * \`package.json\` (npm)
+
+### What to Expect
+
+It looks like your repository dependencies are already up-to-date and no Pull Requests will be necessary right away.
+
+---
+
+❓ Got questions? Check out Renovate's [Docs](https://docs.renovatebot.com/), particularly the Getting Started section.
+If you need any further assistance then you can also [request help here](https://github.com/renovatebot/renovate/discussions).
+
+
+---
+
+ - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.
+
+
+---
+
+
+
+<!--renovate-config-hash:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855-->
+"
+`;
+
+exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with footer and header using templating(onboardingRebaseCheckbox="false") 1`] = `
 "This is a header for platform:github
 
 Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.
@@ -60,7 +97,44 @@ And this is a footer for repository:test baseBranch:some-branch
 "
 `;
 
-exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with footer and header with trailing and leading newlines 1`] = `
+exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with footer and header using templating(onboardingRebaseCheckbox="true") 1`] = `
+"This is a header for platform:github
+
+Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.
+
+🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged.
+
+
+
+---
+### Detected Package Files
+
+ * \`package.json\` (npm)
+
+### What to Expect
+
+It looks like your repository dependencies are already up-to-date and no Pull Requests will be necessary right away.
+
+---
+
+❓ Got questions? Check out Renovate's [Docs](https://docs.renovatebot.com/), particularly the Getting Started section.
+If you need any further assistance then you can also [request help here](https://github.com/renovatebot/renovate/discussions).
+
+
+---
+
+ - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.
+
+
+---
+
+And this is a footer for repository:test baseBranch:some-branch
+
+<!--renovate-config-hash:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855-->
+"
+`;
+
+exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with footer and header with trailing and leading newlines(onboardingRebaseCheckbox="false") 1`] = `
 "
 
 This should not be the first line of the PR
@@ -92,5 +166,47 @@ There should be several empty lines at the end of the PR
 
 
 
+"
+`;
+
+exports[`workers/repository/onboarding/pr/index ensureOnboardingPr() creates PR with footer and header with trailing and leading newlines(onboardingRebaseCheckbox="true") 1`] = `
+"
+
+This should not be the first line of the PR
+
+Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.
+
+🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged.
+
+
+
+---
+### Detected Package Files
+
+ * \`package.json\` (npm)
+
+### What to Expect
+
+It looks like your repository dependencies are already up-to-date and no Pull Requests will be necessary right away.
+
+---
+
+❓ Got questions? Check out Renovate's [Docs](https://docs.renovatebot.com/), particularly the Getting Started section.
+If you need any further assistance then you can also [request help here](https://github.com/renovatebot/renovate/discussions).
+
+
+---
+
+ - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.
+
+
+---
+
+There should be several empty lines at the end of the PR
+
+
+
+
+<!--renovate-config-hash:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855-->
 "
 `;
diff --git a/lib/workers/repository/onboarding/pr/index.spec.ts b/lib/workers/repository/onboarding/pr/index.spec.ts
index cb81683a1dcf9b215d057d1bf44b9625f003dc25..8ba376a88003616124d1a2e041a68bff1d1d15e6 100644
--- a/lib/workers/repository/onboarding/pr/index.spec.ts
+++ b/lib/workers/repository/onboarding/pr/index.spec.ts
@@ -10,7 +10,9 @@ import { GlobalConfig } from '../../../../config/global';
 import { logger } from '../../../../logger';
 import type { PackageFile } from '../../../../modules/manager/types';
 import type { Pr } from '../../../../modules/platform';
+import * as memCache from '../../../../util/cache/memory';
 import type { BranchConfig } from '../../../types';
+import { OnboardingState } from '../common';
 import { ensureOnboardingPr } from '.';
 
 jest.mock('../../../../util/git');
@@ -22,10 +24,11 @@ describe('workers/repository/onboarding/pr/index', () => {
     let branches: BranchConfig[];
 
     const bodyStruct = {
-      hash: '8d5d8373c3fc54803f573ea57ded60686a9df8eb0430ad25da281472eed9ce4e',
+      hash: '6aa71f8cb7b1503b883485c8f5bd564b31923b9c7fa765abe2a7338af40e03b1',
     };
 
     beforeEach(() => {
+      memCache.init();
       jest.resetAllMocks();
       config = {
         ...getConfig(),
@@ -45,8 +48,31 @@ describe('workers/repository/onboarding/pr/index', () => {
       await expect(
         ensureOnboardingPr(config, packageFiles, branches)
       ).resolves.not.toThrow();
+      expect(platform.createPr).toHaveBeenCalledTimes(0);
+      expect(platform.updatePr).toHaveBeenCalledTimes(0);
     });
 
+    it.each`
+      onboardingRebaseCheckbox | prUpdateRequested | expected
+      ${false}                 | ${false}          | ${1}
+      ${false}                 | ${true}           | ${1}
+      ${true}                  | ${false}          | ${0}
+      ${true}                  | ${true}           | ${1}
+    `(
+      'breaks early when onboarding ' +
+        '(onboardingRebaseCheckbox="$onboardingRebaseCheckbox", prUpdateRequeste="$prUpdateRequested" )',
+      async ({ onboardingRebaseCheckbox, prUpdateRequested, expected }) => {
+        config.repoIsOnboarded = false;
+        config.onboardingRebaseCheckbox = onboardingRebaseCheckbox;
+        OnboardingState.prUpdateRequested = prUpdateRequested;
+        await expect(
+          ensureOnboardingPr(config, packageFiles, branches)
+        ).resolves.not.toThrow();
+        expect(platform.updatePr).toHaveBeenCalledTimes(0);
+        expect(platform.createPr).toHaveBeenCalledTimes(expected);
+      }
+    );
+
     it('creates PR', async () => {
       await ensureOnboardingPr(config, packageFiles, branches);
       expect(platform.createPr).toHaveBeenCalledTimes(1);
@@ -69,69 +95,111 @@ describe('workers/repository/onboarding/pr/index', () => {
       ]);
     });
 
-    it('creates PR with empty footer and header', async () => {
-      await ensureOnboardingPr(
-        {
-          ...config,
-          prHeader: '',
-          prFooter: '',
-        },
-        packageFiles,
-        branches
-      );
-      expect(platform.createPr).toHaveBeenCalledTimes(1);
-      expect(platform.createPr.mock.calls[0][0].prBody).toMatchSnapshot();
-    });
+    it.each`
+      onboardingRebaseCheckbox
+      ${false}
+      ${true}
+    `(
+      'creates PR with empty footer and header' +
+        '(onboardingRebaseCheckbox="$onboardingRebaseCheckbox")',
+      async ({ onboardingRebaseCheckbox }) => {
+        config.onboardingRebaseCheckbox = onboardingRebaseCheckbox;
+        OnboardingState.prUpdateRequested = true; // case 'false' is tested in "breaks early when onboarding"
+        await ensureOnboardingPr(
+          {
+            ...config,
+            prHeader: '',
+            prFooter: '',
+          },
+          packageFiles,
+          branches
+        );
+        expect(platform.createPr).toHaveBeenCalledTimes(1);
+        expect(platform.createPr.mock.calls[0][0].prBody).toMatchSnapshot();
+      }
+    );
 
-    it('creates PR with footer and header with trailing and leading newlines', async () => {
-      await ensureOnboardingPr(
-        {
-          ...config,
-          prHeader: '\r\r\nThis should not be the first line of the PR',
-          prFooter:
-            'There should be several empty lines at the end of the PR\r\n\n\n',
-        },
-        packageFiles,
-        branches
-      );
-      expect(platform.createPr).toHaveBeenCalledTimes(1);
-      expect(platform.createPr.mock.calls[0][0].prBody).toMatchSnapshot();
-    });
+    it.each`
+      onboardingRebaseCheckbox
+      ${false}
+      ${true}
+    `(
+      'creates PR with footer and header with trailing and leading newlines' +
+        '(onboardingRebaseCheckbox="$onboardingRebaseCheckbox")',
+      async ({ onboardingRebaseCheckbox }) => {
+        config.onboardingRebaseCheckbox = onboardingRebaseCheckbox;
+        OnboardingState.prUpdateRequested = true; // case 'false' is tested in "breaks early when onboarding"
+        await ensureOnboardingPr(
+          {
+            ...config,
+            prHeader: '\r\r\nThis should not be the first line of the PR',
+            prFooter:
+              'There should be several empty lines at the end of the PR\r\n\n\n',
+          },
+          packageFiles,
+          branches
+        );
+        expect(platform.createPr).toHaveBeenCalledTimes(1);
+        expect(platform.createPr.mock.calls[0][0].prBody).toMatchSnapshot();
+      }
+    );
 
-    it('creates PR with footer and header using templating', async () => {
-      config.baseBranch = 'some-branch';
-      config.repository = 'test';
-      await ensureOnboardingPr(
-        {
-          ...config,
-          prHeader: 'This is a header for platform:{{platform}}',
-          prFooter:
-            'And this is a footer for repository:{{repository}} baseBranch:{{baseBranch}}',
-        },
-        packageFiles,
-        branches
-      );
-      expect(platform.createPr).toHaveBeenCalledTimes(1);
-      expect(platform.createPr.mock.calls[0][0].prBody).toMatch(
-        /platform:github/
-      );
-      expect(platform.createPr.mock.calls[0][0].prBody).toMatch(
-        /repository:test/
-      );
-      expect(platform.createPr.mock.calls[0][0].prBody).toMatchSnapshot();
-    });
+    it.each`
+      onboardingRebaseCheckbox
+      ${false}
+      ${true}
+    `(
+      'creates PR with footer and header using templating' +
+        '(onboardingRebaseCheckbox="$onboardingRebaseCheckbox")',
+      async ({ onboardingRebaseCheckbox }) => {
+        config.baseBranch = 'some-branch';
+        config.repository = 'test';
+        config.onboardingRebaseCheckbox = onboardingRebaseCheckbox;
+        OnboardingState.prUpdateRequested = true; // case 'false' is tested in "breaks early when onboarding"
+        await ensureOnboardingPr(
+          {
+            ...config,
+            prHeader: 'This is a header for platform:{{platform}}',
+            prFooter:
+              'And this is a footer for repository:{{repository}} baseBranch:{{baseBranch}}',
+          },
+          packageFiles,
+          branches
+        );
+        expect(platform.createPr).toHaveBeenCalledTimes(1);
+        expect(platform.createPr.mock.calls[0][0].prBody).toMatch(
+          /platform:github/
+        );
+        expect(platform.createPr.mock.calls[0][0].prBody).toMatch(
+          /repository:test/
+        );
+        expect(platform.createPr.mock.calls[0][0].prBody).toMatchSnapshot();
+      }
+    );
 
-    it('returns if PR does not need updating', async () => {
-      platform.getBranchPr.mockResolvedValue(
-        partial<Pr>({
-          title: 'Configure Renovate',
-          bodyStruct,
-        })
-      );
-      await ensureOnboardingPr(config, packageFiles, branches);
-      expect(platform.createPr).toHaveBeenCalledTimes(0);
-      expect(platform.updatePr).toHaveBeenCalledTimes(0);
-    });
+    it.each`
+      onboardingRebaseCheckbox
+      ${false}
+      ${true}
+    `(
+      'returns if PR does not need updating' +
+        '(onboardingRebaseCheckbox="$onboardingRebaseCheckbox")',
+      async ({ onboardingRebaseCheckbox }) => {
+        const hash =
+          '8d5d8373c3fc54803f573ea57ded60686a9df8eb0430ad25da281472eed9ce4e'; // no rebase checkbox PR hash
+        config.onboardingRebaseCheckbox = onboardingRebaseCheckbox;
+        OnboardingState.prUpdateRequested = true; // case 'false' is tested in "breaks early when onboarding"
+        platform.getBranchPr.mockResolvedValue(
+          partial<Pr>({
+            title: 'Configure Renovate',
+            bodyStruct: onboardingRebaseCheckbox ? bodyStruct : { hash },
+          })
+        );
+        await ensureOnboardingPr(config, packageFiles, branches);
+        expect(platform.createPr).toHaveBeenCalledTimes(0);
+        expect(platform.updatePr).toHaveBeenCalledTimes(0);
+      }
+    );
 
     it('updates PR when conflicted', async () => {
       config.baseBranch = 'some-branch';
diff --git a/lib/workers/repository/onboarding/pr/index.ts b/lib/workers/repository/onboarding/pr/index.ts
index 1c1c41f35e2ee518f23f4a23cc83ef6d0345e782..e562fe95ea5cf688afc7a910ffe60ce698b78d2c 100644
--- a/lib/workers/repository/onboarding/pr/index.ts
+++ b/lib/workers/repository/onboarding/pr/index.ts
@@ -8,9 +8,11 @@ import { hashBody } from '../../../../modules/platform/pr-body';
 import { emojify } from '../../../../util/emoji';
 import {
   deleteBranch,
+  getFile,
   isBranchConflicted,
   isBranchModified,
 } from '../../../../util/git';
+import { toSha256 } from '../../../../util/hasha';
 import * as template from '../../../../util/template';
 import type { BranchConfig } from '../../../types';
 import {
@@ -21,6 +23,7 @@ import {
 import { getPlatformPrOptions } from '../../update/pr';
 import { prepareLabels } from '../../update/pr/labels';
 import { addParticipants } from '../../update/pr/participants';
+import { OnboardingState, defaultConfigFile } from '../common';
 import { getBaseBranchDesc } from './base-branch';
 import { getConfigDesc } from './config-description';
 import { getPrList } from './pr-list';
@@ -30,13 +33,18 @@ export async function ensureOnboardingPr(
   packageFiles: Record<string, PackageFile[]> | null,
   branches: BranchConfig[]
 ): Promise<void> {
-  if (config.repoIsOnboarded) {
+  if (
+    config.repoIsOnboarded ||
+    (config.onboardingRebaseCheckbox && !OnboardingState.prUpdateRequested)
+  ) {
     return;
   }
   logger.debug('ensureOnboardingPr()');
   logger.trace({ config });
   // TODO #7154
   const existingPr = await platform.getBranchPr(config.onboardingBranch!);
+  const { rebaseCheckBox, renovateConfigHashComment } =
+    await getRebaseCheckboxComponents(config);
   logger.debug('Filling in onboarding PR template');
   let prTemplate = `Welcome to [Renovate](${
     config.productLinks!.homepage
@@ -71,6 +79,7 @@ If you need any further assistance then you can also [request help here](${
     }).
 `
   );
+  prTemplate += rebaseCheckBox;
   let prBody = prTemplate;
   if (packageFiles && Object.entries(packageFiles).length) {
     let files: string[] = [];
@@ -126,6 +135,9 @@ If you need any further assistance then you can also [request help here](${
   if (is.string(config.prFooter)) {
     prBody = `${prBody}\n---\n\n${template.compile(config.prFooter, config)}\n`;
   }
+
+  prBody += renovateConfigHashComment;
+
   logger.trace('prBody:\n' + prBody);
 
   prBody = platform.massageMarkdown(prBody);
@@ -186,3 +198,30 @@ If you need any further assistance then you can also [request help here](${
     throw err;
   }
 }
+
+interface RebaseCheckboxComponents {
+  rebaseCheckBox: string;
+  renovateConfigHashComment: string;
+}
+
+async function getRebaseCheckboxComponents(
+  config: RenovateConfig
+): Promise<RebaseCheckboxComponents> {
+  let rebaseCheckBox = '';
+  let renovateConfigHashComment = '';
+  if (!config.onboardingRebaseCheckbox) {
+    return { rebaseCheckBox, renovateConfigHashComment };
+  }
+
+  // Create markdown checkbox
+  rebaseCheckBox = `\n\n---\n\n - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox.\n`;
+
+  // Create hashMeta
+  const configFile = defaultConfigFile(config);
+  const existingContents =
+    (await getFile(configFile, config.onboardingBranch)) ?? '';
+  const hash = toSha256(existingContents);
+  renovateConfigHashComment = `\n<!--renovate-config-hash:${hash}-->\n`;
+
+  return { rebaseCheckBox, renovateConfigHashComment };
+}