diff --git a/tools/docs/datasources.ts b/tools/docs/datasources.ts
index 9d069ff49f79ce23c31ca40a3a2054b6f8fe41a4..aaec81fa262914ba9e343c1b81788486215e58ff 100644
--- a/tools/docs/datasources.ts
+++ b/tools/docs/datasources.ts
@@ -1,6 +1,7 @@
 import { codeBlock } from 'common-tags';
 import { getDatasources } from '../../lib/modules/datasource';
 import { readFile, updateFile } from '../utils';
+import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items';
 import {
   formatDescription,
   formatUrls,
@@ -9,7 +10,10 @@ import {
   replaceContent,
 } from './utils';
 
-export async function generateDatasources(dist: string): Promise<void> {
+export async function generateDatasources(
+  dist: string,
+  datasourceIssuesMap: OpenItems
+): Promise<void> {
   const dsList = getDatasources();
   let datasourceContent = '\nSupported values for `datasource` are:\n\n';
 
@@ -56,6 +60,8 @@ export async function generateDatasources(dist: string): Promise<void> {
         '\n```\n';
     }
 
+    md += generateFeatureAndBugMarkdown(datasourceIssuesMap, datasource);
+
     await updateFile(`${dist}/modules/datasource/${datasource}/index.md`, md);
   }
 
diff --git a/tools/docs/github-query-items.ts b/tools/docs/github-query-items.ts
index f9668ce59bbe18a63eb09fb4454a07a74dd276af..c03461c5f979a92a47d7b1430bcb9d2cc38601cb 100644
--- a/tools/docs/github-query-items.ts
+++ b/tools/docs/github-query-items.ts
@@ -1,4 +1,21 @@
-export type GithubApiQueryResponse = {
+import { DateTime } from 'luxon';
+import { logger } from '../../lib/logger';
+import * as hostRules from '../../lib/util/host-rules';
+import { GithubHttp } from '../../lib/util/http/github';
+import { getQueryString } from '../../lib/util/url';
+
+const gitHubApiUrl = 'https://api.github.com/search/issues?';
+const githubApi = new GithubHttp();
+
+if (process.env.GITHUB_TOKEN) {
+  logger.debug('Using GITHUB_TOKEN from env');
+  hostRules.add({
+    matchHost: 'api.github.com',
+    token: process.env.GITHUB_TOKEN,
+  });
+}
+
+type GithubApiQueryResponse = {
   total_count: number;
   incomplete_results: boolean;
   items: ItemsEntity[];
@@ -14,3 +31,124 @@ export type ItemsEntity = {
 export type LabelsEntity = {
   name: string;
 };
+
+export interface RenovateOpenItems {
+  managers: OpenItems;
+  platforms: OpenItems;
+  datasources: OpenItems;
+}
+
+export type OpenItems = Record<string, Items | undefined>;
+
+export interface Items {
+  bugs: ItemsEntity[];
+  features: ItemsEntity[];
+}
+
+export async function getOpenGitHubItems(): Promise<RenovateOpenItems> {
+  const q = `repo:renovatebot/renovate type:issue is:open -label:priority-5-triage`;
+  const per_page = 100;
+  try {
+    const query = getQueryString({ q, per_page });
+    const res = await githubApi.getJson<GithubApiQueryResponse>(
+      gitHubApiUrl + query,
+      {
+        paginationField: 'items',
+        paginate: true,
+      }
+    );
+    const rawItems = res.body?.items ?? [];
+
+    const renovateOpenItems: RenovateOpenItems = {
+      managers: extractIssues(rawItems, 'manager:'),
+      platforms: extractIssues(rawItems, 'platform:'),
+      datasources: extractIssues(rawItems, 'datasource:'),
+    };
+
+    return renovateOpenItems;
+  } catch (err) {
+    logger.error({ err }, 'Error getting query results');
+    throw err;
+  }
+}
+
+function extractIssues(items: ItemsEntity[], labelPrefix: string): OpenItems {
+  const issuesMap: OpenItems = {};
+
+  for (const item of items) {
+    const type = item.labels
+      .find((l) => l.name.startsWith('type:'))
+      ?.name.split(':')[1];
+    if (!type) {
+      continue;
+    }
+    const label = item.labels
+      .find((l) => l.name.startsWith(labelPrefix))
+      ?.name.split(':')[1];
+    if (!label) {
+      continue;
+    }
+    if (!issuesMap[label]) {
+      issuesMap[label] = { bugs: [], features: [] };
+    }
+    switch (type) {
+      case 'bug':
+        issuesMap[label]?.bugs.push(item);
+        break;
+      case 'feature':
+        issuesMap[label]?.features.push(item);
+        break;
+      default:
+        break;
+    }
+  }
+
+  return issuesMap;
+}
+
+function stringifyIssues(items: ItemsEntity[] | undefined): string {
+  if (!items) {
+    return '';
+  }
+  let list = '';
+  for (const item of items) {
+    list += ` - ${item.title} [#${item.number}](${item.html_url})\n`;
+  }
+  return list;
+}
+
+export function generateFeatureAndBugMarkdown(
+  issuesMap: OpenItems,
+  key: string
+): string {
+  let md = '\n\n';
+
+  const featureList = stringifyIssues(issuesMap[key]?.features);
+  const bugList = stringifyIssues(issuesMap[key]?.bugs);
+
+  if (featureList || bugList) {
+    md += '## Open items\n\n';
+  }
+
+  if (featureList || bugList) {
+    const now = DateTime.utc().toFormat('MMMM dd, yyyy');
+    const lists = `list of ${featureList ? 'features' : ''}${
+      featureList && bugList ? ' and ' : ''
+    }${bugList ? 'bugs' : ''}`;
+    md += `The below ${lists} were current when this page was generated on ${now}.\n\n`;
+  }
+
+  if (featureList) {
+    md += '### Feature requests\n\n';
+    md += featureList;
+    md += '\n';
+  }
+
+  if (bugList) {
+    md += '### Bug reports\n\n';
+    md += bugList;
+    md += '\n';
+  }
+
+  return md;
+}
diff --git a/tools/docs/manager.ts b/tools/docs/manager.ts
index a111f87e3bca18129c254dfcfb0cc5a94f75ea17..dddd4511f925ffa7f0a643819147a6cd237d88e4 100644
--- a/tools/docs/manager.ts
+++ b/tools/docs/manager.ts
@@ -1,29 +1,9 @@
-import { DateTime } from 'luxon';
 import type { RenovateConfig } from '../../lib/config/types';
-import { logger } from '../../lib/logger';
 import { getManagers } from '../../lib/modules/manager';
-import * as hostRules from '../../lib/util/host-rules';
-import { GithubHttp } from '../../lib/util/http/github';
-import { getQueryString } from '../../lib/util/url';
 import { readFile, updateFile } from '../utils';
-import type { GithubApiQueryResponse, ItemsEntity } from './github-query-items';
+import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items';
 import { getDisplayName, getNameWithUrl, replaceContent } from './utils';
 
-const gitHubApiUrl = 'https://api.github.com/search/issues?';
-
-if (process.env.GITHUB_TOKEN) {
-  logger.debug('Using GITHUB_TOKEN from env');
-  hostRules.add({
-    matchHost: 'api.github.com',
-    token: process.env.GITHUB_TOKEN,
-  });
-}
-
-interface ManagerIssues {
-  bugs: ItemsEntity[];
-  features: ItemsEntity[];
-}
-
 function getTitle(manager: string, displayName: string): string {
   if (manager === 'regex') {
     return `Custom Manager Support using Regex`;
@@ -35,84 +15,12 @@ function getManagerLink(manager: string): string {
   return `[\`${manager}\`](${manager}/)`;
 }
 
-function stringifyIssues(items: ItemsEntity[]): [string, number] {
-  if (!items) {
-    return ['', 0];
-  }
-  let list = '';
-  for (const item of items) {
-    list += ` - ${item.title} [#${item.number}](${item.html_url})\n`;
-  }
-  return [list, items.length];
-}
-
-function extractIssues(
-  managerIssuesMap: Record<string, ManagerIssues>,
-  items: ItemsEntity[]
-): void {
-  if (!items || !managerIssuesMap) {
-    return;
-  }
-  for (const item of items) {
-    const type = item.labels
-      .find((l) => l.name.startsWith('type:'))
-      ?.name.split(':')[1];
-    if (!type) {
-      continue;
-    }
-    const manager = item.labels
-      .find((l) => l.name.startsWith('manager:'))
-      ?.name.split(':')[1];
-    if (!manager) {
-      continue;
-    }
-    if (!managerIssuesMap[manager]) {
-      managerIssuesMap[manager] = { bugs: [], features: [] };
-    }
-    switch (type) {
-      case 'bug':
-        managerIssuesMap[manager].bugs.push(item);
-        break;
-      case 'feature':
-        managerIssuesMap[manager].features.push(item);
-        break;
-      default:
-        break;
-    }
-  }
-}
-
-export async function getManagersGitHubIssues(): Promise<
-  Record<string, ManagerIssues>
-> {
-  const q = `repo:renovatebot/renovate type:issue is:open -label:priority-5-triage`;
-  const per_page = 100;
-  const managerIssuesMap: Record<string, ManagerIssues> = {};
-  const githubApi = new GithubHttp('manager-issues');
-  try {
-    const query = getQueryString({ q, per_page });
-    const res = await githubApi.getJson<GithubApiQueryResponse>(
-      gitHubApiUrl + query,
-      {
-        paginationField: 'items',
-        paginate: true,
-      }
-    );
-    const items = res.body?.items ?? [];
-    extractIssues(
-      managerIssuesMap,
-      items.sort((a, b) => a.number - b.number)
-    );
-  } catch (err) {
-    logger.error({ err }, 'Error getting query results');
-    throw err;
-  }
-  return managerIssuesMap;
-}
-
-export async function generateManagers(dist: string): Promise<void> {
+export async function generateManagers(
+  dist: string,
+  managerIssuesMap: OpenItems
+): Promise<void> {
   const managers = getManagers();
-  const managerIssuesMap = await getManagersGitHubIssues();
+
   const allLanguages: Record<string, string[]> = {};
   for (const [manager, definition] of managers) {
     const language = definition.language ?? 'other';
@@ -173,30 +81,9 @@ sidebar_label: ${displayName}
     if (manager !== 'regex') {
       md += '\n## Additional Information\n\n';
     }
-    md += managerReadmeContent + '\n\n';
-
-    const [featureList] = stringifyIssues(managerIssuesMap[manager]?.features);
-    if (featureList) {
-      md += '## Open feature requests\n\n';
-      md += featureList;
-      md += '\n';
-    }
+    md += managerReadmeContent;
 
-    const [bugList] = stringifyIssues(managerIssuesMap[manager]?.bugs);
-    if (bugList) {
-      md += '## Open bug reports\n\n';
-      md += bugList;
-      md += '\n';
-    }
-
-    if (featureList || bugList) {
-      const now = DateTime.utc().toFormat('MMMM dd, yyyy');
-      const lists = `list of ${featureList ? 'features' : ''}${
-        featureList && bugList ? ' and ' : ''
-      }${bugList ? 'bugs' : ''}`;
-      md += '\n\n';
-      md += `The above ${lists} were current when this page was generated on ${now}.\n`;
-    }
+    md += generateFeatureAndBugMarkdown(managerIssuesMap, manager);
 
     await updateFile(`${dist}/modules/manager/${manager}/index.md`, md);
   }
diff --git a/tools/docs/platforms.ts b/tools/docs/platforms.ts
index e6ac36e1d4d62f2a700ec162620498a4f551bf5d..aa83fa273b6bbe1bd27232d0bc0498c1a681a956 100644
--- a/tools/docs/platforms.ts
+++ b/tools/docs/platforms.ts
@@ -1,13 +1,20 @@
 import { getPlatformList } from '../../lib/modules/platform';
 import { readFile, updateFile } from '../utils';
+import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items';
 import { getModuleLink, replaceContent } from './utils';
 
-export async function generatePlatforms(dist: string): Promise<void> {
+export async function generatePlatforms(
+  dist: string,
+  platformIssuesMap: OpenItems
+): Promise<void> {
   let platformContent = 'Supported values for `platform` are: ';
   const platforms = getPlatformList();
   for (const platform of platforms) {
-    const readme = await readFile(`lib/modules/platform/${platform}/index.md`);
-    await updateFile(`${dist}/modules/platform/${platform}/index.md`, readme);
+    let md = await readFile(`lib/modules/platform/${platform}/index.md`);
+
+    md += generateFeatureAndBugMarkdown(platformIssuesMap, platform);
+
+    await updateFile(`${dist}/modules/platform/${platform}/index.md`, md);
   }
 
   platformContent += platforms
@@ -16,8 +23,7 @@ export async function generatePlatforms(dist: string): Promise<void> {
 
   platformContent += '.\n';
 
-  const indexFileName = `docs/usage/modules/platform/index.md`;
-  let indexContent = await readFile(indexFileName);
+  let indexContent = await readFile(`docs/usage/modules/platform/index.md`);
   indexContent = replaceContent(indexContent, platformContent);
   await updateFile(`${dist}/modules/platform/index.md`, indexContent);
 }
diff --git a/tools/generate-docs.ts b/tools/generate-docs.ts
index d4434bf67d293319a3c0a638abf880ba0410728d..6b3f707ba092c8bdb6b33c6c9a4f1ebc89aad68e 100644
--- a/tools/generate-docs.ts
+++ b/tools/generate-docs.ts
@@ -3,6 +3,7 @@ import shell from 'shelljs';
 import { getProblems, logger } from '../lib/logger';
 import { generateConfig } from './docs/config';
 import { generateDatasources } from './docs/datasources';
+import { getOpenGitHubItems } from './docs/github-query-items';
 import { generateManagers } from './docs/manager';
 import { generateManagerAsdfSupportedPlugins } from './docs/manager-asdf-supported-plugins';
 import { generatePlatforms } from './docs/platforms';
@@ -36,8 +37,11 @@ process.on('unhandledRejection', (err) => {
       return;
     }
 
+    logger.info('* fetching open github issues');
+    const openItems = await getOpenGitHubItems();
+
     logger.info('* platforms');
-    await generatePlatforms(dist);
+    await generatePlatforms(dist, openItems.platforms);
 
     // versionings
     logger.info('* versionings');
@@ -45,11 +49,11 @@ process.on('unhandledRejection', (err) => {
 
     // datasources
     logger.info('* datasources');
-    await generateDatasources(dist);
+    await generateDatasources(dist, openItems.datasources);
 
     // managers
     logger.info('* managers');
-    await generateManagers(dist);
+    await generateManagers(dist, openItems.managers);
 
     // managers/asdf supported plugins
     logger.info('* managers/asdf/supported-plugins');