diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md
index 279077a95f833397ef88d4959456afaf07de23ca..8bb68d14ec1cbbf2d8aa8707c39c71cdde964033 100644
--- a/docs/usage/self-hosted-configuration.md
+++ b/docs/usage/self-hosted-configuration.md
@@ -130,6 +130,15 @@ e.g.
 
 This configuration will be applied after all other environment variables so that it can be used to override defaults.
 
+## detectGlobalManagerConfig
+
+The purpose of this capability is to allow a bot admin to configure manager-specific files such as a global `.npmrc` file, instead of configuring it in Renovate config.
+
+This feature is disabled by default because it may prove surprising or undesirable for some users who don't expect Renovate to go into their home directory and import registry or credential information.
+
+Currently this capability is supported for the `npm` manager only - specifically the `~/.npmrc` file.
+If found, it will be imported into `config.npmrc` with `config.npmrcMerge` will be set to `true`.
+
 ## dockerChildPrefix
 
 Adds a custom prefix to the default Renovate sidecar Docker containers name and label.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 90105c4e4de306a2eeca332db1cdd8e88eda983c..382a560472d5f415b38c101b7d08241041fd764b 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -7,6 +7,14 @@ import * as pep440Versioning from '../../versioning/pep440';
 import type { RenovateOptions } from '../types';
 
 const options: RenovateOptions[] = [
+  {
+    name: 'detectGlobalManagerConfig',
+    description:
+      'If true, Renovate will attempt to read global manager config from the file system.',
+    type: 'boolean',
+    default: false,
+    globalOnly: true,
+  },
   {
     name: 'allowPostUpgradeCommandTemplating',
     description: 'If true allow templating for post-upgrade commands.',
diff --git a/lib/manager/index.spec.ts b/lib/manager/index.spec.ts
index 36bdc755041541251b8d56e372b2e3d10c593200..b3eaa34b18e60f382187234bbb71688bf0bf7d48 100644
--- a/lib/manager/index.spec.ts
+++ b/lib/manager/index.spec.ts
@@ -2,6 +2,8 @@ import { loadModules } from '../util/modules';
 import type { ManagerApi } from './types';
 import * as manager from '.';
 
+jest.mock('../util/fs');
+
 describe('manager/index', () => {
   describe('get()', () => {
     it('gets something', () => {
@@ -43,6 +45,12 @@ describe('manager/index', () => {
     }
   });
 
+  describe('detectGlobalConfig()', () => {
+    it('iterates through managers', async () => {
+      expect(await manager.detectAllGlobalConfig()).toEqual({});
+    });
+  });
+
   describe('extractAllPackageFiles()', () => {
     it('returns null', async () => {
       manager.getManagers().set('dummy', {
diff --git a/lib/manager/index.ts b/lib/manager/index.ts
index b4122323dc8dc2eae369e927d653cc8a54070d0f..17419622725485245f158eb70548772d1dd4f925 100644
--- a/lib/manager/index.ts
+++ b/lib/manager/index.ts
@@ -15,6 +15,7 @@ import type { RangeStrategy } from '../types';
 import managers from './api';
 import type {
   ExtractConfig,
+  GlobalManagerConfig,
   ManagerApi,
   PackageFile,
   RangeConfig,
@@ -47,6 +48,18 @@ export const getLanguageList = (): string[] => languageList;
 export const getManagerList = (): string[] => managerList;
 export const getManagers = (): Map<string, ManagerApi> => managers;
 
+export async function detectAllGlobalConfig(): Promise<GlobalManagerConfig> {
+  let config: GlobalManagerConfig = {};
+  for (const managerName of managerList) {
+    const manager = managers.get(managerName);
+    if (manager.detectGlobalConfig) {
+      // This should use mergeChildConfig once more than one manager is supported, but introduces a cyclic dependency
+      config = { ...config, ...(await manager.detectGlobalConfig()) };
+    }
+  }
+  return config;
+}
+
 export async function extractAllPackageFiles(
   manager: string,
   config: ExtractConfig,
diff --git a/lib/manager/npm/detect.spec.ts b/lib/manager/npm/detect.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b997f7f3bab3e5cf52e27f7ca89e0293ac7921da
--- /dev/null
+++ b/lib/manager/npm/detect.spec.ts
@@ -0,0 +1,29 @@
+import { fs } from '../../../test/util';
+import { detectGlobalConfig } from './detect';
+
+jest.mock('../../util/fs');
+
+describe('manager/npm/detect', () => {
+  describe('.detectGlobalConfig()', () => {
+    it('detects .npmrc in home directory', async () => {
+      fs.readFile.mockResolvedValueOnce(
+        'registry=https://registry.npmjs.org\n'
+      );
+      const res = await detectGlobalConfig();
+      expect(res).toMatchInlineSnapshot(`
+Object {
+  "npmrc": "registry=https://registry.npmjs.org
+",
+  "npmrcMerge": true,
+}
+`);
+      expect(res.npmrc).toBeDefined();
+      expect(res.npmrcMerge).toBe(true);
+    });
+    it('handles no .npmrc', async () => {
+      fs.readFile.mockImplementationOnce(() => Promise.reject());
+      const res = await detectGlobalConfig();
+      expect(res).toEqual({});
+    });
+  });
+});
diff --git a/lib/manager/npm/detect.ts b/lib/manager/npm/detect.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f0ca2e72c57e259d1c68e422b702cc94e8bc7163
--- /dev/null
+++ b/lib/manager/npm/detect.ts
@@ -0,0 +1,23 @@
+import os from 'os';
+import is from '@sindresorhus/is';
+import { join } from 'upath';
+import { logger } from '../../logger';
+import { readFile } from '../../util/fs';
+import { GlobalManagerConfig } from '../types';
+
+export async function detectGlobalConfig(): Promise<GlobalManagerConfig> {
+  const res: GlobalManagerConfig = {};
+  const homedir = os.homedir();
+  const npmrcFileName = join(homedir, '.npmrc');
+  try {
+    const npmrc = await readFile(npmrcFileName, 'utf8');
+    if (is.nonEmptyString(npmrc)) {
+      res.npmrc = npmrc;
+      res.npmrcMerge = true;
+      logger.debug(`Detected ${npmrcFileName} and adding it to global config`);
+    }
+  } catch (err) {
+    logger.warn({ npmrcFileName }, 'Error reading .npmrc file');
+  }
+  return res;
+}
diff --git a/lib/manager/npm/index.ts b/lib/manager/npm/index.ts
index 0fc2489fbded064a55c4f1d354512c6a9afe24d3..fdccb12bc16cbe054ebf724a3175034268c0a4cd 100644
--- a/lib/manager/npm/index.ts
+++ b/lib/manager/npm/index.ts
@@ -1,6 +1,7 @@
 import { LANGUAGE_JAVASCRIPT } from '../../constants/languages';
 import * as npmVersioning from '../../versioning/npm';
 
+export { detectGlobalConfig } from './detect';
 export { extractAllPackageFiles } from './extract';
 export {
   bumpPackageVersion,
diff --git a/lib/manager/types.ts b/lib/manager/types.ts
index 5e1842c53d4130703f573980eb4817b3f807eee9..bb512be8b2546a9a770cf1f9bfdf87a722f0cfef 100644
--- a/lib/manager/types.ts
+++ b/lib/manager/types.ts
@@ -220,6 +220,11 @@ export interface UpdateLockedConfig {
   newVersion?: string;
 }
 
+export interface GlobalManagerConfig {
+  npmrc?: string;
+  npmrcMerge?: boolean;
+}
+
 export interface ManagerApi {
   defaultConfig: Record<string, unknown>;
   language?: string;
@@ -231,6 +236,8 @@ export interface ManagerApi {
     bumpVersion: ReleaseType | string
   ): Result<BumpPackageVersionResult>;
 
+  detectGlobalConfig?(): Result<GlobalManagerConfig>;
+
   extractAllPackageFiles?(
     config: ExtractConfig,
     files: string[]
diff --git a/lib/workers/global/config/parse/index.spec.ts b/lib/workers/global/config/parse/index.spec.ts
index 5a75c6724e70e35851c15757e40432df770fcfca..b69f970372915a2548519c4fdc372668e93e1bd2 100644
--- a/lib/workers/global/config/parse/index.spec.ts
+++ b/lib/workers/global/config/parse/index.spec.ts
@@ -3,6 +3,8 @@ import { readFile } from '../../../../util/fs';
 import getArgv from './__fixtures__/argv';
 
 jest.mock('../../../../datasource/npm');
+jest.mock('../../../../util/fs');
+
 try {
   jest.mock('../../config.js');
 } catch (err) {
@@ -118,5 +120,10 @@ describe('workers/global/config/parse/index', () => {
       const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv);
       expect(parsed.endpoint).toEqual('https://github.renovatebot.com/api/v3/');
     });
+    it('parses global manager config', async () => {
+      defaultArgv = defaultArgv.concat(['--detect-global-manager-config=true']);
+      const parsed = await configParser.parseConfigs(defaultEnv, defaultArgv);
+      expect(parsed.npmrc).toBeNull();
+    });
   });
 });
diff --git a/lib/workers/global/config/parse/index.ts b/lib/workers/global/config/parse/index.ts
index 7f9bcc757b99eba5e486b8414d3369c1cc9b4491..e6b35f742bbbb970a3c460cb37f5604760fa4298 100644
--- a/lib/workers/global/config/parse/index.ts
+++ b/lib/workers/global/config/parse/index.ts
@@ -2,6 +2,7 @@ import * as defaultsParser from '../../../../config/defaults';
 import { AllConfig } from '../../../../config/types';
 import { mergeChildConfig } from '../../../../config/utils';
 import { addStream, logger, setContext } from '../../../../logger';
+import { detectAllGlobalConfig } from '../../../../manager';
 import { ensureDir, getSubDirectory, readFile } from '../../../../util/fs';
 import { ensureTrailingSlash } from '../../../../util/url';
 import * as cliParser from './cli';
@@ -73,6 +74,13 @@ export async function parseConfigs(
   logger.debug({ config: envConfig }, 'Env config');
   logger.debug({ config: combinedConfig }, 'Combined config');
 
+  if (config.detectGlobalManagerConfig) {
+    logger.debug('Detecting global manager config');
+    const globalManagerConfig = await detectAllGlobalConfig();
+    logger.debug({ config: globalManagerConfig }, 'Global manager config');
+    config = mergeChildConfig(config, globalManagerConfig);
+  }
+
   // Get global config
   logger.trace({ config }, 'Full config');