diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md
index 3a1f3ba379735ca5ae668d0d83576deb07470231..5d9d208e8ce076c8382437fea621121edd629437 100644
--- a/docs/usage/self-hosted-configuration.md
+++ b/docs/usage/self-hosted-configuration.md
@@ -110,6 +110,10 @@ Set this to `false` if (a) you configure Renovate entirely on the bot side (i.e.
 
 Note that this setting is independent of `branchPrefix`. For example, if you configure `branchPrefix` to be `renovate-` then you'd still have the onboarding PR created with branch `renovate/configure` until you configure `onboardingBranch=renovate-configure` or similar. If you have an existing Renovate installation and you change `onboardingBranch` then it's possible that you'll get onboarding PRs for repositories that had previously closed the onboarding PR unmerged.
 
+## onboardingCommitMessage
+
+Note that if `commitMessagePrefix` or `semanticCommits` values are defined then they will be prepended to the commit message using the same logic that is used for adding them to non-onboarding commit messages.
+
 ## onboardingConfig
 
 ## onboardingPrTitle
diff --git a/lib/config/common.ts b/lib/config/common.ts
index f09944becb39a2729bfb4f566389b52c31b619f9..8bce196e2b8bc18b3b03911bc657184fe1b26619 100644
--- a/lib/config/common.ts
+++ b/lib/config/common.ts
@@ -23,6 +23,7 @@ export interface RenovateSharedConfig {
   branchName?: string;
   manager?: string;
   commitMessage?: string;
+  commitMessagePrefix?: string;
   draftPR?: boolean;
   enabled?: boolean;
   enabledManagers?: string[];
@@ -86,6 +87,7 @@ export interface RenovateAdminConfig {
 
   onboarding?: boolean;
   onboardingBranch?: string;
+  onboardingCommitMessage?: string;
   onboardingPrTitle?: string;
   onboardingConfig?: RenovateSharedConfig;
 
diff --git a/lib/config/definitions.ts b/lib/config/definitions.ts
index 6a90038328671f5a85b34f69cb369e33d87fa252..c16b0d498c03b19aa11f9108f076c43f2550d5cc 100644
--- a/lib/config/definitions.ts
+++ b/lib/config/definitions.ts
@@ -140,6 +140,15 @@ const options: RenovateOptions[] = [
     admin: true,
     cli: false,
   },
+  {
+    name: 'onboardingCommitMessage',
+    description:
+      'Change this value in order to override the default onboarding commit message.',
+    type: 'string',
+    default: null,
+    admin: true,
+    cli: false,
+  },
   {
     name: 'onboardingPrTitle',
     description:
diff --git a/lib/workers/repository/onboarding/branch/create.spec.ts b/lib/workers/repository/onboarding/branch/create.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..282a88965b1a0b9a1212159351d5307cd7c72820
--- /dev/null
+++ b/lib/workers/repository/onboarding/branch/create.spec.ts
@@ -0,0 +1,98 @@
+import { RenovateConfig, getConfig } from '../../../../../test/util';
+import { commitFiles } from '../../../../util/git';
+import { COMMIT_MESSAGE_PREFIX_SEPARATOR } from '../../util/commit-message';
+import { createOnboardingBranch } from './create';
+
+jest.mock('../../../../util/git');
+jest.mock('./config', () => ({
+  getOnboardingConfig: () =>
+    JSON.stringify({
+      foo: 'bar',
+    }),
+}));
+
+const buildExpectedCommitFilesArgument = (message: string) => ({
+  branchName: 'renovate/configure',
+  files: [
+    {
+      name: 'renovate.json',
+      contents: '{"foo":"bar"}',
+    },
+  ],
+  message,
+});
+
+describe('workers/repository/onboarding/branch', () => {
+  let config: RenovateConfig;
+  beforeEach(() => {
+    jest.clearAllMocks();
+    config = getConfig();
+  });
+  describe('createOnboardingBranch', () => {
+    it('applies the default commit message', async () => {
+      await createOnboardingBranch(config);
+      expect(commitFiles).toHaveBeenCalledWith(
+        buildExpectedCommitFilesArgument('Add renovate.json')
+      );
+    });
+    it('applies supplied commit message', async () => {
+      const message =
+        'We can Renovate if we want to, we can leave PRs in decline';
+      config.onboardingCommitMessage = message;
+      await createOnboardingBranch(config);
+      expect(commitFiles).toHaveBeenCalledWith(
+        buildExpectedCommitFilesArgument(`${message}`)
+      );
+    });
+    describe('applies the commitMessagePrefix value', () => {
+      it('to the default commit message', async () => {
+        const prefix = 'RENOV-123';
+        config.commitMessagePrefix = prefix;
+        await createOnboardingBranch(config);
+        expect(commitFiles).toHaveBeenCalledWith(
+          buildExpectedCommitFilesArgument(
+            `${prefix}${COMMIT_MESSAGE_PREFIX_SEPARATOR} add renovate.json`
+          )
+        );
+      });
+      it('to the supplied commit message', async () => {
+        const prefix = 'RENOV-123';
+        const message =
+          "Cause your deps need an update and if they dont update, well they're no deps of mine";
+        config.commitMessagePrefix = prefix;
+        config.onboardingCommitMessage = message;
+        await createOnboardingBranch(config);
+        expect(commitFiles).toHaveBeenCalledWith(
+          buildExpectedCommitFilesArgument(
+            `${prefix}${COMMIT_MESSAGE_PREFIX_SEPARATOR} ${message}`
+          )
+        );
+      });
+    });
+    describe('applies semanticCommit prefix', () => {
+      it('to the default commit message', async () => {
+        const prefix = 'chore(deps)';
+        config.semanticCommits = true;
+        await createOnboardingBranch(config);
+        expect(commitFiles).toHaveBeenCalledWith(
+          buildExpectedCommitFilesArgument(
+            `${prefix}${COMMIT_MESSAGE_PREFIX_SEPARATOR} add renovate.json`
+          )
+        );
+      });
+      it('to the supplied commit message', async () => {
+        const prefix = 'chore(deps)';
+        const message =
+          'I say, we can update when we want to, a commit they will never mind';
+        config.semanticCommits = true;
+        config.onboardingCommitMessage = message;
+        await createOnboardingBranch(config);
+        expect(commitFiles).toHaveBeenCalledWith(
+          buildExpectedCommitFilesArgument(
+            `${prefix}${COMMIT_MESSAGE_PREFIX_SEPARATOR} ${message}`
+          )
+        );
+      });
+    });
+  });
+});
diff --git a/lib/workers/repository/onboarding/branch/create.ts b/lib/workers/repository/onboarding/branch/create.ts
index 615e332457ab41d072e50be8d9eb75c7c62712e0..1777fc57a1e4e251db3c9cd79281ef8176817e42 100644
--- a/lib/workers/repository/onboarding/branch/create.ts
+++ b/lib/workers/repository/onboarding/branch/create.ts
@@ -2,6 +2,7 @@ import { RenovateConfig } from '../../../../config';
 import { configFileNames } from '../../../../config/app-strings';
 import { logger } from '../../../../logger';
 import { commitFiles } from '../../../../util/git';
+import { formatCommitMessagePrefix } from '../../util/commit-message';
 import { getOnboardingConfig } from './config';
 
 const defaultConfigFile = configFileNames[0];
@@ -12,18 +13,31 @@ export function createOnboardingBranch(
   logger.debug('createOnboardingBranch()');
   const contents = getOnboardingConfig(config);
   logger.debug('Creating onboarding branch');
-  let commitMessage;
-  // istanbul ignore if
-  if (config.semanticCommits) {
-    commitMessage = config.semanticCommitType;
+
+  let commitMessagePrefix = '';
+  if (config.commitMessagePrefix) {
+    commitMessagePrefix = config.commitMessagePrefix;
+  } else if (config.semanticCommits) {
+    commitMessagePrefix = config.semanticCommitType;
     if (config.semanticCommitScope) {
-      commitMessage += `(${config.semanticCommitScope})`;
+      commitMessagePrefix += `(${config.semanticCommitScope})`;
     }
-    commitMessage += ': ';
-    commitMessage += 'add ' + defaultConfigFile;
+  }
+  if (commitMessagePrefix) {
+    commitMessagePrefix = formatCommitMessagePrefix(commitMessagePrefix);
+  }
+
+  let onboardingCommitMessage: string;
+  if (config.onboardingCommitMessage) {
+    onboardingCommitMessage = config.onboardingCommitMessage;
   } else {
-    commitMessage = 'Add ' + defaultConfigFile;
+    onboardingCommitMessage = `${
+      commitMessagePrefix ? 'add' : 'Add'
+    } ${defaultConfigFile}`;
   }
+
+  const commitMessage = `${commitMessagePrefix} ${onboardingCommitMessage}`.trim();
+
   // istanbul ignore if
   if (config.dryRun) {
     logger.info('DRY-RUN: Would commit files to onboarding branch');
diff --git a/lib/workers/repository/updates/generate.ts b/lib/workers/repository/updates/generate.ts
index c3c652679387a7c93d2d896ddc2d2198b5526d76..e115b0a19469f18ffd53823356b932cb1b576b67 100644
--- a/lib/workers/repository/updates/generate.ts
+++ b/lib/workers/repository/updates/generate.ts
@@ -7,6 +7,7 @@ import { logger } from '../../../logger';
 import { sanitize } from '../../../util/sanitize';
 import * as template from '../../../util/template';
 import { BranchConfig, BranchUpgradeConfig } from '../../common';
+import { formatCommitMessagePrefix } from '../util/commit-message';
 
 function isTypesGroup(branchUpgrades: any[]): boolean {
   return (
@@ -181,8 +182,7 @@ export function generateBranchConfig(
           upgrade
         )})`;
       }
-      upgrade.commitMessagePrefix = semanticPrefix;
-      upgrade.commitMessagePrefix += semanticPrefix.endsWith(':') ? ' ' : ': ';
+      upgrade.commitMessagePrefix = formatCommitMessagePrefix(semanticPrefix);
       upgrade.toLowerCase =
         // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
         upgrade.semanticCommitType.match(/[A-Z]/) === null &&
diff --git a/lib/workers/repository/util/commit-message.spec.ts b/lib/workers/repository/util/commit-message.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e04be8f6ac0d086e1cd2d13cccf9ddba8d54077
--- /dev/null
+++ b/lib/workers/repository/util/commit-message.spec.ts
@@ -0,0 +1,40 @@
+import {
+  COMMIT_MESSAGE_PREFIX_SEPARATOR,
+  formatCommitMessagePrefix,
+} from './commit-message';
+
+describe('workers/repository/util/commit-message', () => {
+  describe('COMMIT_MESSAGE_PREFIX_END_CHARACTER', () => {
+    it('is a colon character', () => {
+      expect(COMMIT_MESSAGE_PREFIX_SEPARATOR).toBe(':');
+    });
+  });
+  describe('formatCommitMessagePrefix', () => {
+    it.each([
+      [
+        'adds a separator',
+        'does not end',
+        'RENOV-123',
+        `RENOV-123${COMMIT_MESSAGE_PREFIX_SEPARATOR}`,
+      ],
+      [
+        'does nothing',
+        'ends',
+        `RENOV-123${COMMIT_MESSAGE_PREFIX_SEPARATOR}`,
+        `RENOV-123${COMMIT_MESSAGE_PREFIX_SEPARATOR}`,
+      ],
+    ])(
+      '%s when the prefix %s with a separator',
+      (
+        expectedAction: string,
+        endingState: string,
+        commitMessagePrefix: string,
+        expectedPrefix: string
+      ) => {
+        expect(formatCommitMessagePrefix(commitMessagePrefix)).toBe(
+          expectedPrefix
+        );
+      }
+    );
+  });
+});
diff --git a/lib/workers/repository/util/commit-message.ts b/lib/workers/repository/util/commit-message.ts
new file mode 100644
index 0000000000000000000000000000000000000000..014eadda43248c1b70e58ca8b41dfbfdd85961ac
--- /dev/null
+++ b/lib/workers/repository/util/commit-message.ts
@@ -0,0 +1,11 @@
+export const COMMIT_MESSAGE_PREFIX_SEPARATOR = ':';
+
+export const formatCommitMessagePrefix = (
+  commitMessagePrefix: string
+): string => {
+  return `${commitMessagePrefix}${
+    commitMessagePrefix.endsWith(COMMIT_MESSAGE_PREFIX_SEPARATOR)
+      ? ''
+      : COMMIT_MESSAGE_PREFIX_SEPARATOR
+  }`;
+};