diff --git a/lib/modules/platform/gerrit/client.spec.ts b/lib/modules/platform/gerrit/client.spec.ts
index 59a5dcd788ca218c737ce6b446fb6696d6d8ebe3..4901128bb858b139b789e6757ce7f0090bca9560 100644
--- a/lib/modules/platform/gerrit/client.spec.ts
+++ b/lib/modules/platform/gerrit/client.spec.ts
@@ -98,7 +98,11 @@ describe('modules/platform/gerrit/client', () => {
       ['owner:self', { branchName: 'dependency-xyz' }],
       ['project:repo', { branchName: 'dependency-xyz' }],
       ['-is:wip', { branchName: 'dependency-xyz' }],
-      ['hashtag:sourceBranch-dependency-xyz', { branchName: 'dependency-xyz' }],
+      [
+        'footer:Renovate-Branch=dependency-xyz',
+        { branchName: 'dependency-xyz' },
+      ],
+      ['hashtag:sourceBranch-dependency-xyz', { branchName: 'dependency-xyz' }], // for backwards compatibility
       ['label:Code-Review=-2', { branchName: 'dependency-xyz', label: '-2' }],
       [
         'branch:otherTarget',
diff --git a/lib/modules/platform/gerrit/client.ts b/lib/modules/platform/gerrit/client.ts
index 3182db64db37b2f93e69abd719a7db0f5cb319a4..cd2dd1fc30bec6e67146e64cb5043853f8a1c625 100644
--- a/lib/modules/platform/gerrit/client.ts
+++ b/lib/modules/platform/gerrit/client.ts
@@ -231,8 +231,17 @@ class GerritClient {
   ): string[] {
     const filterState = mapPrStateToGerritFilter(searchConfig.state);
     const filters = ['owner:self', 'project:' + repository, filterState];
-    if (searchConfig.branchName !== '') {
-      filters.push(`hashtag:sourceBranch-${searchConfig.branchName}`);
+    if (searchConfig.branchName) {
+      filters.push(
+        ...[
+          '(',
+          `footer:Renovate-Branch=${searchConfig.branchName}`,
+          // for backwards compatibility
+          'OR',
+          `hashtag:sourceBranch-${searchConfig.branchName}`,
+          ')',
+        ],
+      );
     }
     if (searchConfig.targetBranch) {
       filters.push(`branch:${searchConfig.targetBranch}`);
diff --git a/lib/modules/platform/gerrit/readme.md b/lib/modules/platform/gerrit/readme.md
index 01a539311cf65dadb456f15247cebaf2c8eb531c..7246f3f946dc7873a6f9b8bedbe787f6e52643ba 100644
--- a/lib/modules/platform/gerrit/readme.md
+++ b/lib/modules/platform/gerrit/readme.md
@@ -3,11 +3,16 @@
 ## Supported Gerrit versions
 
 Renovate supports all Gerrit 3.x versions.
+
 Support for Gerrit is currently _experimental_, meaning that it _might_ still have some undiscovered bugs or design limitations, and that we _might_ need to change functionality in a non-backwards compatible manner in a non-major release.
 
-The current implementation uses Gerrit's "hashtags" feature.
-Therefore you must use a Gerrit version that uses the [NoteDB](https://gerrit-review.googlesource.com/Documentation/note-db.html) backend.
-We did not test Gerrit `2.x` with NoteDB (only in `2.15` and `2.16`), but could work.
+Renovate stores its metadata in the _commit message footer_.
+
+Previously Renovate stored metadata in Gerrit's _hashtags_.
+To keep backwards compatibility, Renovate still reads metadata from hashtags.
+But Renovate _always_ puts its metadata in the _commit message footer_!
+When the Renovate maintainers mark Gerrit support as stable, the maintainers will remove the "read metadata from hashtags" feature.
+This means changes without metadata in the commit message footer will be "forgotten" by Renovate.
 
 ## Authentication
 
diff --git a/lib/modules/platform/gerrit/scm.spec.ts b/lib/modules/platform/gerrit/scm.spec.ts
index 18667a04c4d4c5457c77cdb6607f7fcbf5499f97..9be155245ad15e524370b698e40fe70dd3b8bfb0 100644
--- a/lib/modules/platform/gerrit/scm.spec.ts
+++ b/lib/modules/platform/gerrit/scm.spec.ts
@@ -300,13 +300,18 @@ describe('modules/platform/gerrit/scm', () => {
         baseBranch: 'main',
         branchName: 'renovate/dependency-1.x',
         files: [],
-        message: ['commit msg', expect.stringMatching(/Change-Id: I.{32}/)],
+        message: [
+          'commit msg',
+          expect.stringMatching(
+            /^Renovate-Branch: renovate\/dependency-1\.x\nChange-Id: I[a-z0-9]{40}$/,
+          ),
+        ],
         force: true,
       });
       expect(git.pushCommit).toHaveBeenCalledWith({
         files: [],
         sourceRef: 'renovate/dependency-1.x',
-        targetRef: 'refs/for/main%t=sourceBranch-renovate/dependency-1.x',
+        targetRef: 'refs/for/main',
       });
     });
 
@@ -339,7 +344,10 @@ describe('modules/platform/gerrit/scm', () => {
         baseBranch: 'main',
         branchName: 'renovate/dependency-1.x',
         files: [],
-        message: ['commit msg', 'Change-Id: ...'],
+        message: [
+          'commit msg',
+          'Renovate-Branch: renovate/dependency-1.x\nChange-Id: ...',
+        ],
         force: true,
       });
       expect(git.fetchRevSpec).toHaveBeenCalledWith('refs/changes/1/2');
@@ -377,14 +385,17 @@ describe('modules/platform/gerrit/scm', () => {
         baseBranch: 'main',
         branchName: 'renovate/dependency-1.x',
         files: [],
-        message: ['commit msg', 'Change-Id: ...'],
+        message: [
+          'commit msg',
+          'Renovate-Branch: renovate/dependency-1.x\nChange-Id: ...',
+        ],
         force: true,
       });
       expect(git.fetchRevSpec).toHaveBeenCalledWith('refs/changes/1/2');
       expect(git.pushCommit).toHaveBeenCalledWith({
         files: [],
         sourceRef: 'renovate/dependency-1.x',
-        targetRef: 'refs/for/main%t=sourceBranch-renovate/dependency-1.x',
+        targetRef: 'refs/for/main',
       });
       expect(clientMock.wasApprovedBy).toHaveBeenCalledWith(
         existingChange,
diff --git a/lib/modules/platform/gerrit/scm.ts b/lib/modules/platform/gerrit/scm.ts
index f4369fa39e2877af8b10e2c2b29598fbc7399ea0..d8500e952052514fa02a8a551b8823030e53dc3f 100644
--- a/lib/modules/platform/gerrit/scm.ts
+++ b/lib/modules/platform/gerrit/scm.ts
@@ -109,7 +109,7 @@ export class GerritScm extends DefaultGitScm {
       typeof commit.message === 'string' ? [commit.message] : commit.message;
     commit.message = [
       ...origMsg,
-      `Change-Id: ${existingChange?.change_id ?? generateChangeId()}`,
+      `Renovate-Branch: ${commit.branchName}\nChange-Id: ${existingChange?.change_id ?? generateChangeId()}`,
     ];
     const commitResult = await git.prepareCommit({ ...commit, force: true });
     if (commitResult) {
@@ -123,9 +123,7 @@ export class GerritScm extends DefaultGitScm {
       if (hasChanges || commit.force) {
         const pushResult = await git.pushCommit({
           sourceRef: commit.branchName,
-          targetRef: `refs/for/${commit.baseBranch!}%t=sourceBranch-${
-            commit.branchName
-          }`,
+          targetRef: `refs/for/${commit.baseBranch!}`,
           files: commit.files,
         });
         if (pushResult) {
diff --git a/lib/modules/platform/gerrit/types.ts b/lib/modules/platform/gerrit/types.ts
index 3aae7d47e6831e5adbacbc44d69bf7d31bbf758c..0d1b5d90fed354cf02f9acc4a3ef2fc918717af8 100644
--- a/lib/modules/platform/gerrit/types.ts
+++ b/lib/modules/platform/gerrit/types.ts
@@ -34,6 +34,9 @@ export type GerritReviewersType = 'REVIEWER' | 'CC' | 'REMOVED';
 
 export interface GerritChange {
   branch: string;
+  /**
+   * for backwards compatibility
+   */
   hashtags?: string[];
   change_id: string;
   subject: string;
diff --git a/lib/modules/platform/gerrit/utils.spec.ts b/lib/modules/platform/gerrit/utils.spec.ts
index f5159804473bea13cf054d4cbcb7ce9e3da95f90..b609fdf7a88c0e094f1ae0fbdeceb75b3e8c92aa 100644
--- a/lib/modules/platform/gerrit/utils.spec.ts
+++ b/lib/modules/platform/gerrit/utils.spec.ts
@@ -10,6 +10,7 @@ import type {
   GerritChangeMessageInfo,
   GerritChangeStatus,
   GerritLabelTypeInfo,
+  GerritRevisionInfo,
 } from './types';
 import * as utils from './utils';
 import { mapBranchStatusToLabel } from './utils';
@@ -83,7 +84,6 @@ describe('modules/platform/gerrit/utils', () => {
       const change = partial<GerritChange>({
         _number: 123456,
         status: 'NEW',
-        hashtags: ['other', 'sourceBranch-renovate/dependency-1.x'],
         branch: 'main',
         subject: 'Fix for',
         reviewers: {
@@ -91,6 +91,15 @@ describe('modules/platform/gerrit/utils', () => {
           REMOVED: [],
           CC: [],
         },
+        current_revision: 'abc',
+        revisions: {
+          abc: partial<GerritRevisionInfo>({
+            commit: {
+              message:
+                'Some change\n\nRenovate-Branch: renovate/dependency-1.x\nChange-Id: ...',
+            },
+          }),
+        },
         messages: [
           partial<GerritChangeMessageInfo>({
             id: '9d78ac236714cee8c2d86e95d638358925cf6853',
@@ -122,11 +131,10 @@ describe('modules/platform/gerrit/utils', () => {
       });
     });
 
-    it('map a gerrit change without sourceBranch-tag and reviewers to Pr', () => {
+    it('map a gerrit change without source branch info and reviewers to Pr', () => {
       const change = partial<GerritChange>({
         _number: 123456,
         status: 'NEW',
-        hashtags: ['other'],
         branch: 'main',
         subject: 'Fix for',
       });
@@ -145,26 +153,80 @@ describe('modules/platform/gerrit/utils', () => {
   });
 
   describe('extractSourceBranch()', () => {
-    it('without hashtags', () => {
+    it('no commit message', () => {
+      const change = partial<GerritChange>();
+      expect(utils.extractSourceBranch(change)).toBeUndefined();
+    });
+
+    it('commit message with no footer', () => {
       const change = partial<GerritChange>({
-        hashtags: undefined,
+        current_revision: 'abc',
+        revisions: {
+          abc: partial<GerritRevisionInfo>({
+            commit: {
+              message: 'some message...',
+            },
+          }),
+        },
       });
       expect(utils.extractSourceBranch(change)).toBeUndefined();
     });
 
-    it('no hashtag with "sourceBranch-" prefix', () => {
+    it('commit message with footer', () => {
       const change = partial<GerritChange>({
-        hashtags: ['other', 'another'],
+        current_revision: 'abc',
+        revisions: {
+          abc: partial<GerritRevisionInfo>({
+            commit: {
+              message:
+                'Some change\n\nRenovate-Branch: renovate/dependency-1.x\nChange-Id: ...',
+            },
+          }),
+        },
       });
-      expect(utils.extractSourceBranch(change)).toBeUndefined();
+      expect(utils.extractSourceBranch(change)).toBe('renovate/dependency-1.x');
+    });
+
+    // for backwards compatibility
+    it('no commit message but with hashtags', () => {
+      const change = partial<GerritChange>({
+        hashtags: ['sourceBranch-renovate/dependency-1.x'],
+      });
+      expect(utils.extractSourceBranch(change)).toBe('renovate/dependency-1.x');
     });
 
-    it('hashtag with "sourceBranch-" prefix', () => {
+    // for backwards compatibility
+    it('commit message with no footer but with hashtags', () => {
       const change = partial<GerritChange>({
-        hashtags: ['other', 'sourceBranch-renovate/dependency-1.x', 'another'],
+        hashtags: ['sourceBranch-renovate/dependency-1.x'],
+        current_revision: 'abc',
+        revisions: {
+          abc: partial<GerritRevisionInfo>({
+            commit: {
+              message: 'some message...',
+            },
+          }),
+        },
       });
       expect(utils.extractSourceBranch(change)).toBe('renovate/dependency-1.x');
     });
+
+    // for backwards compatibility
+    it('prefers the footer over the hashtags', () => {
+      const change = partial<GerritChange>({
+        hashtags: ['sourceBranch-renovate/dependency-1.x'],
+        current_revision: 'abc',
+        revisions: {
+          abc: partial<GerritRevisionInfo>({
+            commit: {
+              message:
+                'Some change\n\nRenovate-Branch: renovate/dependency-2.x\nChange-Id: ...',
+            },
+          }),
+        },
+      });
+      expect(utils.extractSourceBranch(change)).toBe('renovate/dependency-2.x');
+    });
   });
 
   describe('findPullRequestBody()', () => {
diff --git a/lib/modules/platform/gerrit/utils.ts b/lib/modules/platform/gerrit/utils.ts
index d42ec4a463b2e1b47a6b35986763522cd7895c6f..3eb28e4b8ece4790121307e8500761a4769fddc6 100644
--- a/lib/modules/platform/gerrit/utils.ts
+++ b/lib/modules/platform/gerrit/utils.ts
@@ -2,6 +2,7 @@ import { CONFIG_GIT_URL_UNAVAILABLE } from '../../../constants/error-messages';
 import { logger } from '../../../logger';
 import type { BranchStatus, PrState } from '../../../types';
 import * as hostRules from '../../../util/host-rules';
+import { regEx } from '../../../util/regex';
 import { joinUrlParts, parseUrl } from '../../../util/url';
 import { hashBody } from '../pr-body';
 import type { Pr } from '../types';
@@ -90,9 +91,24 @@ export function mapGerritChangeStateToPrState(
   return 'all';
 }
 export function extractSourceBranch(change: GerritChange): string | undefined {
-  return change.hashtags
-    ?.find((tag) => tag.startsWith('sourceBranch-'))
-    ?.replace('sourceBranch-', '');
+  let sourceBranch: string | undefined = undefined;
+
+  if (change.current_revision) {
+    const re = regEx(/^Renovate-Branch: (.+)$/m);
+    const message = change.revisions[change.current_revision]?.commit?.message;
+    if (message) {
+      sourceBranch = re.exec(message)?.[1];
+    }
+  }
+
+  // for backwards compatibility
+  if (!sourceBranch) {
+    sourceBranch = change.hashtags
+      ?.find((tag) => tag.startsWith('sourceBranch-'))
+      ?.replace('sourceBranch-', '');
+  }
+
+  return sourceBranch ?? undefined;
 }
 
 export function findPullRequestBody(change: GerritChange): string | undefined {