diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md
index f3d6b0999743e4e07aca0eaca2a74db4f19e1ada..df2ac655a260bbf99a336fd4426ee2e147c402c3 100644
--- a/docs/usage/self-hosted-configuration.md
+++ b/docs/usage/self-hosted-configuration.md
@@ -126,6 +126,11 @@ If using negations, all repositories except those who match the regex are added
 }
 ```
 
+## autodiscoverTopics
+
+Some platforms allow you to add tags, or topics, to repositories and retrieve repository lists by specifying those
+topics. Set this variable to a list of strings, all of which will be topics for the autodiscovered repositories.
+
 ## baseDir
 
 By default Renovate uses a temporary directory like `/tmp/renovate` to store its data.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 061d43406688649b74607155e0093a7a278267a0..95761e5f55f03498f72feb47b302346e3c5f6f77 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -765,6 +765,16 @@ const options: RenovateOptions[] = [
     default: null,
     globalOnly: true,
   },
+  {
+    name: 'autodiscoverTopics',
+    description: '',
+    stage: 'global',
+    type: 'array',
+    subType: 'string',
+    default: null,
+    globalOnly: true,
+    supportedPlatforms: ['gitlab'],
+  },
   {
     name: 'prCommitsPerRunLimit',
     description:
diff --git a/lib/config/types.ts b/lib/config/types.ts
index ac915cd80304f56a9a80a6aa68928104f396fe8d..488ef1d8ad60428450d538080f57bdc8480e4962 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -94,6 +94,7 @@ export interface RenovateSharedConfig {
 export interface GlobalOnlyConfig {
   autodiscover?: boolean;
   autodiscoverFilter?: string[] | string;
+  autodiscoverTopics?: string[];
   baseDir?: string;
   cacheDir?: string;
   containerbaseDir?: string;
diff --git a/lib/modules/platform/gitlab/index.spec.ts b/lib/modules/platform/gitlab/index.spec.ts
index 75959e84d29dfbdc29edc807a08d553f32a90e7a..0530d65e250683bbffd845c3885bf6dba5f529f6 100644
--- a/lib/modules/platform/gitlab/index.spec.ts
+++ b/lib/modules/platform/gitlab/index.spec.ts
@@ -163,6 +163,24 @@ describe('modules/platform/gitlab/index', () => {
       const repos = await gitlab.getRepos();
       expect(repos).toEqual(['a/b', 'c/d']);
     });
+
+    it('should encode the requested topics into the URL', async () => {
+      httpMock
+        .scope(gitlabApiHost)
+        .get(
+          '/api/v4/projects?membership=true&per_page=100&with_merge_requests_enabled=true&min_access_level=30&archived=false&topic=one%2Ctwo'
+        )
+        .reply(200, [
+          {
+            path_with_namespace: 'a/b',
+          },
+          {
+            path_with_namespace: 'c/d',
+          },
+        ]);
+      const repos = await gitlab.getRepos({ topics: ['one', 'two'] });
+      expect(repos).toEqual(['a/b', 'c/d']);
+    });
   });
 
   async function initRepo(
diff --git a/lib/modules/platform/gitlab/index.ts b/lib/modules/platform/gitlab/index.ts
index 8817d5178d73420809fa10ab52646d8584dbb101..88fbf9f1f0ce222f80c0f7bcc8a323656eff32a8 100644
--- a/lib/modules/platform/gitlab/index.ts
+++ b/lib/modules/platform/gitlab/index.ts
@@ -31,6 +31,7 @@ import {
 } from '../../../util/url';
 import { getPrBodyStruct } from '../pr-body';
 import type {
+  AutodiscoverConfig,
   BranchStatusConfig,
   CreatePRConfig,
   EnsureCommentConfig,
@@ -142,10 +143,23 @@ export async function initPlatform({
 }
 
 // Get all repositories that the user has access to
-export async function getRepos(): Promise<string[]> {
+export async function getRepos(config?: AutodiscoverConfig): Promise<string[]> {
   logger.debug('Autodiscovering GitLab repositories');
+
+  const queryParams: Record<string, any> = {
+    membership: true,
+    per_page: 100,
+    with_merge_requests_enabled: true,
+    min_access_level: 30,
+    archived: false,
+  };
+  if (config?.topics?.length) {
+    queryParams['topic'] = config.topics.join(',');
+  }
+
+  const url = 'projects?' + getQueryString(queryParams);
+
   try {
-    const url = `projects?membership=true&per_page=100&with_merge_requests_enabled=true&min_access_level=30&archived=false`;
     const res = await gitlabApi.getJson<RepoResponse[]>(url, {
       paginate: true,
     });
diff --git a/lib/modules/platform/types.ts b/lib/modules/platform/types.ts
index d9bc8f84caa40d8e8cebf538972869686e5b3d73..e96f96286a3640867552f17b0a199637bacf32dc 100644
--- a/lib/modules/platform/types.ts
+++ b/lib/modules/platform/types.ts
@@ -164,6 +164,10 @@ export type EnsureCommentRemovalConfig =
 
 export type EnsureIssueResult = 'updated' | 'created';
 
+export interface AutodiscoverConfig {
+  topics?: string[];
+}
+
 export interface Platform {
   findIssue(title: string): Promise<Issue | null>;
   getIssueList(): Promise<Issue[]>;
@@ -191,7 +195,7 @@ export interface Platform {
   addReviewers(number: number, reviewers: string[]): Promise<void>;
   addAssignees(number: number, assignees: string[]): Promise<void>;
   createPr(prConfig: CreatePRConfig): Promise<Pr | null>;
-  getRepos(): Promise<string[]>;
+  getRepos(config?: AutodiscoverConfig): Promise<string[]>;
   getRepoForceRebase(): Promise<boolean>;
   deleteLabel(number: number, label: string): Promise<void>;
   setBranchStatus(branchStatusConfig: BranchStatusConfig): Promise<void>;
diff --git a/lib/workers/global/autodiscover.ts b/lib/workers/global/autodiscover.ts
index 943f739d67f247d4e96b83772e5cc806a1d55697..2ea7d7953ccdf5ddf107445c127aef84ad593e5b 100644
--- a/lib/workers/global/autodiscover.ts
+++ b/lib/workers/global/autodiscover.ts
@@ -35,7 +35,9 @@ export async function autodiscoverRepositories(
     return config;
   }
   // Autodiscover list of repositories
-  let discovered = await platform.getRepos();
+  let discovered = await platform.getRepos({
+    topics: config.autodiscoverTopics,
+  });
   if (!discovered?.length) {
     // Soft fail (no error thrown) if no accessible repositories
     logger.debug(