diff --git a/lib/config/presets/gitlab.spec.ts b/lib/config/presets/gitlab.spec.ts
index ff3a357901b5bca34e8b2e2fab903bff7c9dba2e..64b6db804b5e0ad1266afd1b1209543911d87b9b 100644
--- a/lib/config/presets/gitlab.spec.ts
+++ b/lib/config/presets/gitlab.spec.ts
@@ -11,7 +11,6 @@ const glGot: jest.Mock<Promise<PartialDeep<GotResponse>>> = api.get as never;
 describe('config/presets/gitlab', () => {
   beforeEach(() => {
     glGot.mockReset();
-    global.repoCache = {};
     return global.renovateCache.rmAll();
   });
   describe('getPreset()', () => {
diff --git a/lib/config/presets/local.spec.ts b/lib/config/presets/local.spec.ts
index 5780818d57289669e19cc88e5ab4e5b755a7467d..2820554006d6eb539d138dd4294460d5cf4480e8 100644
--- a/lib/config/presets/local.spec.ts
+++ b/lib/config/presets/local.spec.ts
@@ -18,7 +18,6 @@ describe('config/presets/local', () => {
     gitlabGetPreset.mockResolvedValueOnce({ resolved: 'preset' });
     githubGetPreset.mockReset();
     githubGetPreset.mockResolvedValueOnce({ resolved: 'preset' });
-    global.repoCache = {};
     return global.renovateCache.rmAll();
   });
   describe('getPreset()', () => {
diff --git a/lib/config/presets/npm.spec.ts b/lib/config/presets/npm.spec.ts
index 868478c0eaf060d185c0e8ab008616d229d1976e..7ba07ce6b8b8937f51afd6259f72337cc4a707b0 100644
--- a/lib/config/presets/npm.spec.ts
+++ b/lib/config/presets/npm.spec.ts
@@ -8,7 +8,6 @@ describe('config/presets/npm', () => {
   delete process.env.NPM_TOKEN;
   beforeEach(() => {
     jest.resetAllMocks();
-    global.repoCache = {};
     global.trustLevel = 'low';
     nock.cleanAll();
     return global.renovateCache.rmAll();
diff --git a/lib/datasource/crate/index.spec.ts b/lib/datasource/crate/index.spec.ts
index 176ab304c9652aa83ebe6b9a98e7e34b72f84b23..ecf31c4e764765196b8d565d5e56815ebce27a22 100644
--- a/lib/datasource/crate/index.spec.ts
+++ b/lib/datasource/crate/index.spec.ts
@@ -19,9 +19,6 @@ jest.mock('../../util/got');
 
 describe('datasource/crate', () => {
   describe('getReleases', () => {
-    beforeEach(() => {
-      global.repoCache = {};
-    });
     it('returns null for empty result', async () => {
       got.mockReturnValueOnce(null);
       expect(
diff --git a/lib/datasource/dart/index.spec.ts b/lib/datasource/dart/index.spec.ts
index e4a63bb018002150d005f8ff31d36e9ecc14f5bc..95124ba2e25f1e2e8ed8fed89ccfde50bb3b982f 100644
--- a/lib/datasource/dart/index.spec.ts
+++ b/lib/datasource/dart/index.spec.ts
@@ -15,9 +15,6 @@ jest.mock('../../util/got');
 
 describe('datasource/dart', () => {
   describe('getReleases', () => {
-    beforeEach(() => {
-      global.repoCache = {};
-    });
     it('returns null for empty result', async () => {
       got.mockReturnValueOnce(null);
       expect(await getReleases({ lookupName: 'non_sense' })).toBeNull();
diff --git a/lib/datasource/docker/index.spec.ts b/lib/datasource/docker/index.spec.ts
index 24909e0b8549e94332269de2093e7f62efb8e8e2..f6620f750e9509a9e8d88e4ca0ba08a76059dff7 100644
--- a/lib/datasource/docker/index.spec.ts
+++ b/lib/datasource/docker/index.spec.ts
@@ -5,6 +5,7 @@ import * as docker from '.';
 import { getPkgReleases } from '..';
 import * as _hostRules from '../../util/host-rules';
 import { DATASOURCE_FAILURE } from '../../constants/error-messages';
+import { clearRepoCache } from '../../util/cache';
 
 const got: any = _got;
 const hostRules: any = _hostRules;
@@ -29,7 +30,7 @@ describe('api/docker', () => {
   describe('getDigest', () => {
     beforeEach(() => {
       jest.resetAllMocks();
-      global.repoCache = {};
+      clearRepoCache();
       hostRules.find.mockReturnValue({
         username: 'some-username',
         password: 'some-password',
@@ -279,7 +280,7 @@ describe('api/docker', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
-      global.repoCache = {};
+      clearRepoCache();
       return global.renovateCache.rmAll();
     });
     it('returns null if no token', async () => {
diff --git a/lib/datasource/galaxy/index.spec.ts b/lib/datasource/galaxy/index.spec.ts
index a902d97f704d80ac7c00bf060d43bcba139c3acc..535f956a9dacf50b0c2538e9f6a222ea692e935b 100644
--- a/lib/datasource/galaxy/index.spec.ts
+++ b/lib/datasource/galaxy/index.spec.ts
@@ -18,9 +18,6 @@ jest.mock('../../util/got');
 
 describe('datasource/galaxy', () => {
   describe('getReleases', () => {
-    beforeEach(() => {
-      global.repoCache = {};
-    });
     it('returns null for empty result', async () => {
       got.mockReturnValueOnce(null);
       expect(
diff --git a/lib/datasource/github-tags/index.spec.ts b/lib/datasource/github-tags/index.spec.ts
index 1655cd3bf64d69ce1dec4b524387437943f21888..8119c027c03478bc267ec0b74d3a45638343cee4 100644
--- a/lib/datasource/github-tags/index.spec.ts
+++ b/lib/datasource/github-tags/index.spec.ts
@@ -16,7 +16,6 @@ describe('datasource/github-tags', () => {
     beforeEach(() => {
       jest.resetAllMocks();
       hostRules.hosts = jest.fn(() => []);
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
     it('returns null if no token', async () => {
diff --git a/lib/datasource/gitlab-tags/index.spec.ts b/lib/datasource/gitlab-tags/index.spec.ts
index baa74f48de909855cc7a72e957f522c9f234a56b..5d345590c238482801a7d0b55bb6c3359fcab676 100644
--- a/lib/datasource/gitlab-tags/index.spec.ts
+++ b/lib/datasource/gitlab-tags/index.spec.ts
@@ -8,7 +8,6 @@ const glGot: any = api.get;
 
 describe('datasource/gitlab-tags', () => {
   beforeEach(() => {
-    global.repoCache = {};
     return global.renovateCache.rmAll();
   });
   describe('getReleases', () => {
diff --git a/lib/datasource/go/index.spec.ts b/lib/datasource/go/index.spec.ts
index 5b3c72a5cde2f175638a7db222f1a598e856e0f4..38d5de888d831557611e3c1f5ca6b73fbdf65b25 100644
--- a/lib/datasource/go/index.spec.ts
+++ b/lib/datasource/go/index.spec.ts
@@ -24,9 +24,6 @@ Nothing to see here; <a href="https://godoc.org/golang.org/x/text">move along</a
 </html>`;
 
 describe('datasource/go', () => {
-  beforeEach(() => {
-    global.repoCache = {};
-  });
   describe('getDigest', () => {
     it('returns null for wrong name', async () => {
       got.mockReturnValueOnce({
diff --git a/lib/datasource/gradle-version/index.spec.ts b/lib/datasource/gradle-version/index.spec.ts
index 23f7351972fd642b3d1fe35fad63e2cf541a9f1b..2df73da159f63bc8fdb96baaa414c40e4c08750a 100644
--- a/lib/datasource/gradle-version/index.spec.ts
+++ b/lib/datasource/gradle-version/index.spec.ts
@@ -19,7 +19,6 @@ describe('datasource/gradle-version', () => {
         lookupName: 'abc',
       };
       jest.clearAllMocks();
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
 
diff --git a/lib/datasource/helm/index.spec.ts b/lib/datasource/helm/index.spec.ts
index 5c7e2eeea6957b7b80636c06522b2ed52085bf97..956c1ccca6bb51e2853ab203dcf45ef5c43e81d7 100644
--- a/lib/datasource/helm/index.spec.ts
+++ b/lib/datasource/helm/index.spec.ts
@@ -16,7 +16,6 @@ describe('datasource/helm', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.resetAllMocks();
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
     it('returns null if lookupName was not provided', async () => {
diff --git a/lib/datasource/hex/index.spec.ts b/lib/datasource/hex/index.spec.ts
index 0a165808e61b8b09fa47a9da840058fd4a82a1a2..9d53a528397ed97736022da5213e446911fce90d 100644
--- a/lib/datasource/hex/index.spec.ts
+++ b/lib/datasource/hex/index.spec.ts
@@ -18,9 +18,6 @@ jest.mock('../../util/host-rules');
 
 describe('datasource/hex', () => {
   describe('getReleases', () => {
-    beforeEach(() => {
-      global.repoCache = {};
-    });
     it('returns null for empty result', async () => {
       got.mockReturnValueOnce(null);
       expect(
diff --git a/lib/datasource/index.ts b/lib/datasource/index.ts
index 7122b79fd49a7b008dfcfbd2c11ec1bd21639ba6..39028e02ec8f39d82e00f0ec6d16c4dbc9857fe3 100644
--- a/lib/datasource/index.ts
+++ b/lib/datasource/index.ts
@@ -14,6 +14,7 @@ import {
 } from './common';
 import datasources from './api.generated';
 import { clone } from '../util/clone';
+import { getRepoCached, setRepoCached } from '../util/cache';
 
 export * from './common';
 
@@ -65,12 +66,11 @@ function getRawReleases(
     config.datasource +
     config.lookupName +
     config.registryUrls;
-  // The repoCache is initialized for each repo
   // By returning a Promise and reusing it, we should only fetch each package at most once
-  if (!global.repoCache[cacheKey]) {
-    global.repoCache[cacheKey] = fetchReleases(config);
+  if (!getRepoCached(cacheKey)) {
+    setRepoCached(cacheKey, fetchReleases(config));
   }
-  return global.repoCache[cacheKey];
+  return getRepoCached<Promise<ReleaseResult | null>>(cacheKey);
 }
 
 export async function getPkgReleases(
diff --git a/lib/datasource/maven/index.spec.ts b/lib/datasource/maven/index.spec.ts
index 0bb37673b382bf957568207a8227d34d170850a8..02891c2948eae8f4090b3f4be1de9e3969154dd6 100644
--- a/lib/datasource/maven/index.spec.ts
+++ b/lib/datasource/maven/index.spec.ts
@@ -39,7 +39,6 @@ describe('datasource/maven', () => {
       password: 'password',
     });
     jest.resetAllMocks();
-    global.repoCache = {};
     nock.cleanAll();
     nock.disableNetConnect();
     nock('https://repo.maven.apache.org')
diff --git a/lib/datasource/npm/index.spec.ts b/lib/datasource/npm/index.spec.ts
index 02c30d180fd65cfc809c5fce20fe31892c6c36ad..bc7b93f78397635b79adb2514d33987917693bfc 100644
--- a/lib/datasource/npm/index.spec.ts
+++ b/lib/datasource/npm/index.spec.ts
@@ -5,6 +5,7 @@ import * as npm from '.';
 import * as hostRules from '../../util/host-rules';
 import { DATASOURCE_FAILURE } from '../../constants/error-messages';
 import { getName } from '../../../test/util';
+import { clearRepoCache } from '../../util/cache';
 
 jest.mock('registry-auth-token');
 jest.mock('delay');
@@ -25,7 +26,7 @@ describe(getName(__filename), () => {
   delete process.env.NPM_TOKEN;
   beforeEach(() => {
     jest.resetAllMocks();
-    global.repoCache = {};
+    clearRepoCache();
     global.trustLevel = 'low';
     npm.resetCache();
     npm.setNpmrc();
diff --git a/lib/datasource/nuget/index.spec.ts b/lib/datasource/nuget/index.spec.ts
index 986835311df6da90797ebd22aa9c13c402f1064f..aa303cc6377071ffbaddace322afd96ba60954ef 100644
--- a/lib/datasource/nuget/index.spec.ts
+++ b/lib/datasource/nuget/index.spec.ts
@@ -87,7 +87,6 @@ describe('datasource/nuget', () => {
     beforeEach(() => {
       jest.resetAllMocks();
       hostRules.hosts = jest.fn(() => []);
-      global.repoCache = {};
     });
 
     it(`can't detect nuget feed version`, async () => {
diff --git a/lib/datasource/orb/index.spec.ts b/lib/datasource/orb/index.spec.ts
index ea5da05f9554b012207739c96faf41b5a402040f..eee0c94d7535218c6701361043772486b952b6b1 100644
--- a/lib/datasource/orb/index.spec.ts
+++ b/lib/datasource/orb/index.spec.ts
@@ -30,7 +30,6 @@ describe('datasource/orb', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
     it('returns null for empty result', async () => {
diff --git a/lib/datasource/packagist/index.spec.ts b/lib/datasource/packagist/index.spec.ts
index 355f976d845f7c1b565d0997fb30af2ff9aabd72..48a15419dd3d5f91a2f06334cc29335fcdc6fb3b 100644
--- a/lib/datasource/packagist/index.spec.ts
+++ b/lib/datasource/packagist/index.spec.ts
@@ -3,6 +3,7 @@ import _got from '../../util/got';
 import * as packagist from '.';
 import * as _hostRules from '../../util/host-rules';
 import * as composerVersioning from '../../versioning/composer';
+import { clearRepoCache } from '../../util/cache';
 
 jest.mock('../../util/got');
 jest.mock('../../util/host-rules');
@@ -27,7 +28,7 @@ describe('datasource/packagist', () => {
       jest.resetAllMocks();
       hostRules.find = jest.fn((input) => input);
       hostRules.hosts = jest.fn(() => []);
-      global.repoCache = {};
+      clearRepoCache();
       config = {
         versioning: composerVersioning.id,
         registryUrls: [
diff --git a/lib/datasource/packagist/index.ts b/lib/datasource/packagist/index.ts
index 80fd6929d2f1c0d48b72a15eb523942108d605d3..4a84c982a8c59ce56ee3d034075aec6b2bb13633 100644
--- a/lib/datasource/packagist/index.ts
+++ b/lib/datasource/packagist/index.ts
@@ -1,12 +1,12 @@
 import is from '@sindresorhus/is';
 
 import URL from 'url';
-import delay from 'delay';
 import pAll from 'p-all';
 import { logger } from '../../logger';
 import { Http, HttpOptions } from '../../util/http';
 import * as hostRules from '../../util/host-rules';
 import { DatasourceError, GetReleasesConfig, ReleaseResult } from '../common';
+import { getRepoCached, setRepoCached } from '../../util/cache';
 
 export const id = 'packagist';
 
@@ -172,19 +172,8 @@ interface AllPackages {
 }
 
 async function getAllPackages(regUrl: string): Promise<AllPackages | null> {
-  let repoCacheResult = global.repoCache[`packagist-${regUrl}`];
-  // istanbul ignore if
-  if (repoCacheResult) {
-    while (repoCacheResult === 'pending') {
-      await delay(200);
-      repoCacheResult = global.repoCache[`packagist-${regUrl}`];
-    }
-    return repoCacheResult;
-  }
-  global.repoCache[`packagist-${regUrl}`] = 'pending';
   const registryMeta = await getRegistryMeta(regUrl);
   if (!registryMeta) {
-    global.repoCache[`packagist-${regUrl}`] = null;
     return null;
   }
   const {
@@ -224,10 +213,17 @@ async function getAllPackages(regUrl: string): Promise<AllPackages | null> {
     providerPackages,
     includesPackages,
   };
-  global.repoCache[`packagist-${regUrl}`] = allPackages;
   return allPackages;
 }
 
+function getAllCachedPackages(regUrl: string): Promise<AllPackages | null> {
+  const cacheKey = `packagist-${regUrl}`;
+  if (getRepoCached(cacheKey) === undefined) {
+    setRepoCached(cacheKey, getAllPackages(regUrl));
+  }
+  return getRepoCached(cacheKey);
+}
+
 async function packagistOrgLookup(name: string): Promise<ReleaseResult> {
   const cacheNamespace = 'datasource-packagist-org';
   const cachedResult = await renovateCache.get<ReleaseResult>(
@@ -262,7 +258,7 @@ async function packageLookup(
       const packagistResult = await packagistOrgLookup(name);
       return packagistResult;
     }
-    const allPackages = await getAllPackages(regUrl);
+    const allPackages = await getAllCachedPackages(regUrl);
     if (!allPackages) {
       return null;
     }
diff --git a/lib/datasource/pod/index.spec.ts b/lib/datasource/pod/index.spec.ts
index 7cec2c8a8a036a4d8d3e5d9f159a564c5c014d9b..d1fda1294a4abdde8b006f5b0afb9a98b12546b8 100644
--- a/lib/datasource/pod/index.spec.ts
+++ b/lib/datasource/pod/index.spec.ts
@@ -4,6 +4,7 @@ import * as rubyVersioning from '../../versioning/ruby';
 import { getPkgReleases } from '..';
 import { mocked } from '../../../test/util';
 import { GotResponse } from '../../platform';
+import { clearRepoCache } from '../../util/cache';
 
 const api = mocked(_api);
 
@@ -20,7 +21,7 @@ describe('datasource/cocoapods', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.resetAllMocks();
-      global.repoCache = {};
+      clearRepoCache();
       return global.renovateCache.rmAll();
     });
 
diff --git a/lib/datasource/pypi/index.spec.ts b/lib/datasource/pypi/index.spec.ts
index 8b696f5f1be4a692c897d3708c9a53806a8d99c5..739c6264bd6757449e055f9fbffb21ccb4ab4784 100644
--- a/lib/datasource/pypi/index.spec.ts
+++ b/lib/datasource/pypi/index.spec.ts
@@ -26,7 +26,6 @@ describe('datasource/pypi', () => {
     beforeEach(() => {
       process.env = { ...OLD_ENV };
       delete process.env.PIP_INDEX_URL;
-      global.repoCache = {};
     });
 
     afterEach(() => {
@@ -35,7 +34,6 @@ describe('datasource/pypi', () => {
 
     beforeEach(() => {
       jest.resetAllMocks();
-      global.repoCache = {};
     });
     it('returns null for empty result', async () => {
       got.mockReturnValueOnce({});
diff --git a/lib/datasource/ruby-version/index.spec.ts b/lib/datasource/ruby-version/index.spec.ts
index 2f26b3f29b0207b951d403d099b812ebf43df425..5db9f38e134adf808da494ad2d66e701e92f27c1 100644
--- a/lib/datasource/ruby-version/index.spec.ts
+++ b/lib/datasource/ruby-version/index.spec.ts
@@ -14,7 +14,6 @@ const rubyReleasesHtml = fs.readFileSync(
 describe('datasource/gradle', () => {
   describe('getReleases', () => {
     beforeEach(() => {
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
     it('parses real data', async () => {
diff --git a/lib/datasource/rubygems/index.spec.ts b/lib/datasource/rubygems/index.spec.ts
index 5247219a4812ab5f27f26ea1ca33f9327b5b9a68..09b63174aead28891c9c46eb6b306b494bd72125 100644
--- a/lib/datasource/rubygems/index.spec.ts
+++ b/lib/datasource/rubygems/index.spec.ts
@@ -4,6 +4,7 @@ import railsVersions from './__fixtures__/rails/versions.json';
 import * as rubyVersioning from '../../versioning/ruby';
 import * as rubygems from '.';
 import { getPkgReleases } from '..';
+import { clearRepoCache } from '../../util/cache';
 
 const got: any = _got;
 
@@ -45,7 +46,7 @@ describe('datasource/rubygems', () => {
     });
 
     afterEach(() => {
-      global.repoCache = {};
+      clearRepoCache();
       process.env.RENOVATE_SKIP_CACHE = SKIP_CACHE;
     });
 
diff --git a/lib/datasource/terraform-module/index.spec.ts b/lib/datasource/terraform-module/index.spec.ts
index a5d9e06dc5bd510ea09f36774fadf2d7200c1522..320f678b363cbee0d6a55837509e4bc1764ab7e8 100644
--- a/lib/datasource/terraform-module/index.spec.ts
+++ b/lib/datasource/terraform-module/index.spec.ts
@@ -14,7 +14,6 @@ describe('datasource/terraform-module', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
     it('returns null for empty result', async () => {
diff --git a/lib/datasource/terraform-provider/index.spec.ts b/lib/datasource/terraform-provider/index.spec.ts
index 7d61edbc22f52a2fa3b02e29b92c07ff8844c596..608e69a8616be2568ff1151b65d0f9e738f0f8e6 100644
--- a/lib/datasource/terraform-provider/index.spec.ts
+++ b/lib/datasource/terraform-provider/index.spec.ts
@@ -14,7 +14,6 @@ describe('datasource/terraform', () => {
   describe('getReleases', () => {
     beforeEach(() => {
       jest.clearAllMocks();
-      global.repoCache = {};
       return global.renovateCache.rmAll();
     });
     it('returns null for empty result', async () => {
diff --git a/lib/globals.d.ts b/lib/globals.d.ts
index b8d91df810d6c7fe100da8f689fd936fcdec1869..4bc09bb772d361cc76b3dcef4fc13812d882a19b 100644
--- a/lib/globals.d.ts
+++ b/lib/globals.d.ts
@@ -33,8 +33,6 @@ declare namespace NodeJS {
 
     renovateCache: Renovate.Cache;
 
-    repoCache: Record<string, any>;
-
     trustLevel?: string;
 
     updateRubyGemsVersions?: Promise<void>;
diff --git a/lib/manager/bundler/artifacts.spec.ts b/lib/manager/bundler/artifacts.spec.ts
index 00de11a3e3660a1fcf4ee5c61ad754325809c6dd..66f25b97c4ea0d8f89ac409a12a6dbf078336a6e 100644
--- a/lib/manager/bundler/artifacts.spec.ts
+++ b/lib/manager/bundler/artifacts.spec.ts
@@ -49,7 +49,7 @@ describe('bundler.updateArtifacts()', () => {
     env.getChildProcessEnv.mockReturnValue(envMock.basic);
     bundlerHostRules.findAllAuthenticatable.mockReturnValue([]);
     resetPrefetchedImages();
-    global.repoCache = {};
+
     await setUtilConfig(config);
   });
   it('returns null by default', async () => {
diff --git a/lib/manager/bundler/artifacts.ts b/lib/manager/bundler/artifacts.ts
index 7042dd8cfd301450997bee692da8a8a3779df089..3be189aebe8079426c639ddb1baf0d07b50bfd69 100644
--- a/lib/manager/bundler/artifacts.ts
+++ b/lib/manager/bundler/artifacts.ts
@@ -16,6 +16,7 @@ import {
   findAllAuthenticatable,
   getDomain,
 } from './host-rules';
+import { getRepoCached, setRepoCached } from '../../util/cache';
 
 const hostConfigVariablePrefix = 'BUNDLE_';
 
@@ -70,12 +71,12 @@ export async function updateArtifacts(
     config,
   } = updateArtifact;
   const { compatibility = {} } = config;
-
   logger.debug(`bundler.updateArtifacts(${packageFileName})`);
+  const existingError = getRepoCached<string>('bundlerArtifactsError');
   // istanbul ignore if
-  if (global.repoCache.bundlerArtifactsError) {
+  if (existingError) {
     logger.debug('Aborting Bundler artifacts due to previous failed attempt');
-    throw new Error(global.repoCache.bundlerArtifactsError);
+    throw new Error(existingError);
   }
   const lockFileName = `${packageFileName}.lock`;
   const existingLockFileContent = await platform.getFile(lockFileName);
@@ -176,7 +177,7 @@ export async function updateArtifacts(
         'Gemfile.lock update failed due to missing credentials - skipping branch'
       );
       // Do not generate these PRs because we don't yet support Bundler authentication
-      global.repoCache.bundlerArtifactsError = BUNDLER_INVALID_CREDENTIALS;
+      setRepoCached('bundlerArtifactsError', BUNDLER_INVALID_CREDENTIALS);
       throw new Error(BUNDLER_INVALID_CREDENTIALS);
     }
     const resolveMatchRe = new RegExp('\\s+(.*) was resolved to', 'g');
diff --git a/lib/util/cache.spec.ts b/lib/util/cache.spec.ts
index 14413bad8b2a61f1776a640d2841c7dd68ee4cc9..cd41ba573e9649b7dc17dd0714a626807bc762d9 100644
--- a/lib/util/cache.spec.ts
+++ b/lib/util/cache.spec.ts
@@ -1,7 +1,11 @@
-import { getRepoCache } from './cache';
+import { clearRepoCache, getRepoCached, setRepoCached } from './cache';
 
 describe('getRepoCache', () => {
-  it('returns the global cache', () => {
-    expect(getRepoCache()).toBeDefined();
+  it('sets and gets repo cache', () => {
+    setRepoCached('key', 'value');
+    expect(getRepoCached('key')).toEqual('value');
+  });
+  it('clears repo cache', () => {
+    clearRepoCache();
   });
 });
diff --git a/lib/util/cache.ts b/lib/util/cache.ts
index d4282d9dedd47148528190fe082b4c6b7596d3b7..1fd3b1863adfd4bbfa0f5c85f6b72bfb3edab049 100644
--- a/lib/util/cache.ts
+++ b/lib/util/cache.ts
@@ -1,8 +1,13 @@
-export function getRepoCache(): Record<string, any> {
-  // eslint-disable-next-line no-return-assign
-  return global.repoCache ?? (global.repoCache = {});
-}
+let repoCache: Record<string, any> = {};
 
 export function clearRepoCache(): void {
-  global.repoCache = {};
+  repoCache = {};
+}
+
+export function getRepoCached<T = any>(key: string): T {
+  return repoCache[key];
+}
+
+export function setRepoCached(key: string, value: any): void {
+  repoCache[key] = value;
 }
diff --git a/lib/util/got/cache-get.ts b/lib/util/got/cache-get.ts
index 803a789ac22cb8115d87f391853a85cdf80391f0..987f05805c2031f5ebb3ac8b4f060171e228c6ec 100644
--- a/lib/util/got/cache-get.ts
+++ b/lib/util/got/cache-get.ts
@@ -1,17 +1,13 @@
 import crypto from 'crypto';
 import { create } from './util';
 import { clone } from '../clone';
+import { getRepoCached, setRepoCached } from '../cache';
 
-// global.repoCache is reset to {} every time a repository is initialized
 // With this caching, it means every GET request is cached during each repository run
 
 export default create({
   options: {},
   handler: (options, next) => {
-    if (!global.repoCache) {
-      return next(options);
-    }
-
     if (options.stream) {
       return next(options);
     }
@@ -26,13 +22,16 @@ export default create({
             JSON.stringify({ href: options.href, headers: options.headers })
         )
         .digest('hex');
-      if (!global.repoCache[cacheKey] || options.useCache === false) {
-        global.repoCache[cacheKey] = next(options).catch((err) => {
-          delete global.repoCache[cacheKey];
-          throw err;
-        });
+      if (!getRepoCached(cacheKey) || options.useCache === false) {
+        setRepoCached(
+          cacheKey,
+          next(options).catch((err) => {
+            setRepoCached(cacheKey, null);
+            throw err;
+          })
+        );
       }
-      return global.repoCache[cacheKey].then((response) => ({
+      return getRepoCached<Promise<any>>(cacheKey).then((response) => ({
         ...response,
         body: clone(response.body),
       }));
diff --git a/lib/util/got/index.spec.ts b/lib/util/got/index.spec.ts
index 514abab0ae58fcc316be2e3760f4acbe5e73d9e1..f061eb65c42beb7d3c088aadb0883ec8c025e851 100644
--- a/lib/util/got/index.spec.ts
+++ b/lib/util/got/index.spec.ts
@@ -8,6 +8,7 @@ import {
   PLATFORM_TYPE_GITLAB,
   PLATFORM_TYPE_GITHUB,
 } from '../../constants/platforms';
+import { clearRepoCache } from '../cache';
 
 const baseUrl = 'https://api.github.com';
 
@@ -19,7 +20,7 @@ describe(getName(__filename), () => {
   afterEach(() => {
     nock.cleanAll();
     hostRules.clear();
-    global.repoCache = {};
+    clearRepoCache();
     nock.enableNetConnect();
   });
 
@@ -66,7 +67,6 @@ describe(getName(__filename), () => {
   it('uses private-token auth', async () => {
     const req = mock({ reqheaders: { 'private-token': 'XXX' } });
     hostRules.add({ baseUrl, token: 'XXX' });
-    global.repoCache = null;
     expect(await got({ hostType: PLATFORM_TYPE_GITLAB })).toMatchSnapshot();
     expect(req.isDone()).toBe(true);
   });
@@ -97,14 +97,13 @@ describe(getName(__filename), () => {
       await got({ hostType: PLATFORM_TYPE_GITHUB, method: 'HEAD' })
     ).toMatchSnapshot();
 
-    global.repoCache = {};
+    clearRepoCache();
 
     await expect(got({ hostType: PLATFORM_TYPE_GITHUB })).rejects.toThrowError(
       'not-found'
     );
 
     expect(req.isDone()).toBe(true);
-    expect(global.repoCache).toEqual({});
   });
 
   it('streams no cache', async () => {
@@ -130,6 +129,5 @@ describe(getName(__filename), () => {
 
     expect(data).toBe('{}');
     expect(req.isDone()).toBe(true);
-    expect(global.repoCache).toEqual({});
   });
 });
diff --git a/lib/util/http/index.spec.ts b/lib/util/http/index.spec.ts
index 5ab2db72adddf811a4b51e4c595155c57e8b87da..78dfc1938c75f23cf69fe7c6f2381488e239b9ec 100644
--- a/lib/util/http/index.spec.ts
+++ b/lib/util/http/index.spec.ts
@@ -79,6 +79,5 @@ describe(getName(__filename), () => {
 
     expect(data).toBe('{}');
     expect(nock.isDone()).toBe(true);
-    expect(global.repoCache).toEqual({});
   });
 });
diff --git a/lib/workers/repository/init/index.ts b/lib/workers/repository/init/index.ts
index d347c6ea93bd49c372992df5e6b0fd859f6ca22b..a43a3d4fb58636cb07dd4e6dfced09bcd73528e2 100644
--- a/lib/workers/repository/init/index.ts
+++ b/lib/workers/repository/init/index.ts
@@ -8,9 +8,10 @@ import { detectSemanticCommits } from './semantic';
 import { detectVulnerabilityAlerts } from './vulnerability';
 import { platform } from '../../../platform';
 import { RenovateConfig } from '../../../config';
+import { clearRepoCache } from '../../../util/cache';
 
 export async function initRepo(input: RenovateConfig): Promise<RenovateConfig> {
-  global.repoCache = {};
+  clearRepoCache();
   let config: RenovateConfig = {
     ...input,
     errors: [],
diff --git a/lib/workers/repository/process/lookup/index.spec.ts b/lib/workers/repository/process/lookup/index.spec.ts
index 698a49a0062b4ba51bcfdb8fd556f92fd7fd0934..d3e901930340cbe8877620e539adf7cf2af4b8b2 100644
--- a/lib/workers/repository/process/lookup/index.spec.ts
+++ b/lib/workers/repository/process/lookup/index.spec.ts
@@ -20,6 +20,7 @@ import * as datasourcePackagist from '../../../../datasource/packagist';
 import * as datasourceDocker from '../../../../datasource/docker';
 import * as datasourceGithubTags from '../../../../datasource/github-tags';
 import * as datasourceGitSubmodules from '../../../../datasource/git-submodules';
+import { clearRepoCache } from '../../../../util/cache';
 
 jest.mock('../../../../datasource/docker');
 jest.mock('../../../../datasource/git-submodules');
@@ -37,7 +38,7 @@ describe('workers/repository/process/lookup', () => {
     config.manager = 'npm';
     config.versioning = npmVersioning.id;
     config.rangeStrategy = 'replace';
-    global.repoCache = {};
+    clearRepoCache();
     jest.resetAllMocks();
   });
 
diff --git a/test/globals.ts b/test/globals.ts
index 52d4ce0ff639ec7b772b7a31f9917471590abe7b..ad44bb591dcbbd0c67bec5ad255697f9889a46b1 100644
--- a/test/globals.ts
+++ b/test/globals.ts
@@ -10,8 +10,6 @@ jest.mock('../lib/platform', () => ({
 }));
 jest.mock('../lib/logger');
 
-global.repoCache = {};
-
 const tmpDir = process.env.RENOVATE_TMPDIR || process.env.TMPDIR || tmpdir();
 const cacheDir = join(tmpDir, './renovate/cache/renovate');