diff --git a/lib/modules/manager/npm/post-update/lerna.spec.ts b/lib/modules/manager/npm/post-update/lerna.spec.ts
index 8b2277a322658010379cb737976ec99eec5f00ae..7be793ba6046f1c72f492b1efe4b5eecf4c6c66c 100644
--- a/lib/modules/manager/npm/post-update/lerna.spec.ts
+++ b/lib/modules/manager/npm/post-update/lerna.spec.ts
@@ -1,12 +1,16 @@
 import { envMock, mockExecAll } from '../../../../../test/exec-util';
-import { env, partial } from '../../../../../test/util';
+import { env, mockedFunction, partial } from '../../../../../test/util';
 import { GlobalConfig } from '../../../../config/global';
 import type { RepoGlobalConfig } from '../../../../config/types';
 import type { PackageFile, PostUpdateConfig } from '../../types';
 import * as lernaHelper from './lerna';
+import { getNodeToolConstraint } from './node-version';
 
 jest.mock('../../../../util/exec/env');
-jest.mock('../../npm/post-update/node-version');
+jest.mock('./node-version');
+jest.mock('../../../datasource');
+
+process.env.BUILDPACK = 'true';
 
 function lernaPkgFile(lernaClient: string): Partial<PackageFile> {
   return {
@@ -26,7 +30,10 @@ function lernaPkgFileWithoutLernaDep(
 const config = partial<PostUpdateConfig>({});
 
 describe('modules/manager/npm/post-update/lerna', () => {
-  const globalConfig: RepoGlobalConfig = { localDir: '' };
+  const globalConfig: RepoGlobalConfig = {
+    localDir: '',
+    cacheDir: '/tmp/cache',
+  };
 
   describe('generateLockFiles()', () => {
     beforeEach(() => {
@@ -34,6 +41,10 @@ describe('modules/manager/npm/post-update/lerna', () => {
       jest.resetModules();
       env.getChildProcessEnv.mockReturnValue(envMock.basic);
       GlobalConfig.set(globalConfig);
+      mockedFunction(getNodeToolConstraint).mockResolvedValueOnce({
+        toolName: 'node',
+        constraint: '16.16.0',
+      });
     });
 
     it('returns if no lernaClient', async () => {
@@ -120,6 +131,89 @@ describe('modules/manager/npm/post-update/lerna', () => {
       expect(res.error).toBeFalse();
       expect(execSnapshots).toMatchSnapshot();
     });
+
+    it('suppports docker', async () => {
+      const execSnapshots = mockExecAll();
+      GlobalConfig.set({ ...globalConfig, binarySource: 'docker' });
+
+      const res = await lernaHelper.generateLockFiles(
+        lernaPkgFile('npm'),
+        'some-dir',
+        { ...config, constraints: { npm: '6.0.0' } },
+        {}
+      );
+      expect(execSnapshots).toMatchObject([
+        {
+          cmd: 'docker pull renovate/sidecar',
+        },
+        {
+          cmd: 'docker ps --filter name=renovate_sidecar -aq',
+        },
+        {
+          cmd:
+            'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
+            '-v "/tmp/cache":"/tmp/cache" ' +
+            '-e BUILDPACK_CACHE_DIR ' +
+            '-w "some-dir" renovate/sidecar ' +
+            'bash -l -c "' +
+            'install-tool node 16.16.0 ' +
+            '&& ' +
+            'install-tool npm 6.0.0 ' +
+            '&& ' +
+            'hash -d npm 2>/dev/null || true ' +
+            '&& ' +
+            'install-tool lerna 2.0.0 ' +
+            '&& ' +
+            'lerna info || echo \\"Ignoring lerna info failure\\" ' +
+            '&& ' +
+            'npm install --ignore-scripts  --no-audit --package-lock-only ' +
+            '&& ' +
+            'lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts  --no-audit --package-lock-only' +
+            '"',
+          options: {
+            cwd: 'some-dir',
+          },
+        },
+      ]);
+      expect(res.error).toBeFalse();
+    });
+
+    it('suppports binarySource=install', async () => {
+      const execSnapshots = mockExecAll();
+      GlobalConfig.set({ ...globalConfig, binarySource: 'install' });
+
+      const res = await lernaHelper.generateLockFiles(
+        lernaPkgFile('npm'),
+        'some-dir',
+        { ...config, constraints: { npm: '6.0.0' } },
+        {}
+      );
+      expect(res.error).toBeFalse();
+      expect(execSnapshots).toMatchObject([
+        { cmd: 'install-tool node 16.16.0' },
+        { cmd: 'install-tool npm 6.0.0' },
+        { cmd: 'hash -d npm 2>/dev/null || true' },
+        { cmd: 'install-tool lerna 2.0.0' },
+        {
+          cmd: 'lerna info || echo "Ignoring lerna info failure"',
+          options: {
+            cwd: 'some-dir',
+          },
+        },
+        {
+          cmd: 'npm install --ignore-scripts  --no-audit --package-lock-only',
+          options: {
+            cwd: 'some-dir',
+          },
+        },
+        {
+          cmd: 'lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts  --no-audit --package-lock-only',
+          options: {
+            cwd: 'some-dir',
+          },
+        },
+      ]);
+    });
   });
 
   describe('getLernaVersion()', () => {
diff --git a/lib/modules/manager/npm/post-update/lerna.ts b/lib/modules/manager/npm/post-update/lerna.ts
index 22d9a8717fb7651c33f86cceba88f5063c775ef2..40c2d6a2b962642ad409ddc11a2e8319f5e30623 100644
--- a/lib/modules/manager/npm/post-update/lerna.ts
+++ b/lib/modules/manager/npm/post-update/lerna.ts
@@ -1,22 +1,24 @@
 import semver from 'semver';
-import { quote } from 'shlex';
 import upath from 'upath';
 import { GlobalConfig } from '../../../../config/global';
 import { TEMPORARY_ERROR } from '../../../../constants/error-messages';
 import { logger } from '../../../../logger';
 import { exec } from '../../../../util/exec';
-import type { ExecOptions, ExtraEnv } from '../../../../util/exec/types';
+import type {
+  ExecOptions,
+  ExtraEnv,
+  ToolConstraint,
+} from '../../../../util/exec/types';
 import type { PackageFile, PostUpdateConfig } from '../../types';
-import { getNodeConstraint } from './node-version';
+import { getNodeToolConstraint } from './node-version';
 import type { GenerateLockFileResult } from './types';
-import { getOptimizeCommand } from './yarn';
 
 // Exported for testability
 export function getLernaVersion(
   lernaPackageFile: Partial<PackageFile>
 ): string {
   const lernaDep = lernaPackageFile.deps?.find((d) => d.depName === 'lerna');
-  if (!lernaDep || !semver.validRange(lernaDep.currentValue)) {
+  if (!lernaDep?.currentValue || !semver.validRange(lernaDep.currentValue)) {
     logger.warn(
       // TODO: types (#7154)
       // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
@@ -24,8 +26,7 @@ export function getLernaVersion(
     );
     return 'latest';
   }
-  // TODO #7154
-  return lernaDep.currentValue!;
+  return lernaDep.currentValue;
 }
 
 export async function generateLockFiles(
@@ -41,28 +42,34 @@ export async function generateLockFiles(
     return { error: false };
   }
   logger.debug(`Spawning lerna with ${lernaClient} to create lock files`);
-  const preCommands: string[] = [];
+  const toolConstraints: ToolConstraint[] = [
+    await getNodeToolConstraint(config, []),
+  ];
   const cmd: string[] = [];
   let cmdOptions = '';
   try {
     if (lernaClient === 'yarn') {
-      let installYarn = 'npm i -g yarn';
+      const yarnTool: ToolConstraint = {
+        toolName: 'yarn',
+        constraint: '^1.22.18', // needs to be a v1 yarn, otherwise v2 will be installed
+      };
       const yarnCompatibility = config.constraints?.yarn;
       if (semver.validRange(yarnCompatibility)) {
-        installYarn += `@${quote(yarnCompatibility)}`;
+        yarnTool.constraint = yarnCompatibility;
       }
-      preCommands.push(installYarn);
+      toolConstraints.push(yarnTool);
       if (skipInstalls !== false) {
-        preCommands.push(getOptimizeCommand());
+        // The following change causes Yarn 1.x to exit gracefully after updating the lock file but without installing node_modules
+        yarnTool.toolName = 'yarn-slim';
       }
       cmdOptions = '--ignore-scripts --ignore-engines --ignore-platform';
     } else if (lernaClient === 'npm') {
-      let installNpm = 'npm i -g npm';
+      const npmTool: ToolConstraint = { toolName: 'npm' };
       const npmCompatibility = config.constraints?.npm;
       if (semver.validRange(npmCompatibility)) {
-        installNpm += `@${quote(npmCompatibility)} || true`;
+        npmTool.constraint = npmCompatibility;
       }
-      preCommands.push(installNpm, 'hash -d npm 2>/dev/null || true');
+      toolConstraints.push(npmTool);
       cmdOptions = '--ignore-scripts  --no-audit';
       if (skipInstalls !== false) {
         cmdOptions += ' --package-lock-only';
@@ -77,7 +84,6 @@ export async function generateLockFiles(
       lernaCommand = lernaCommand.replace('--ignore-scripts ', '');
     }
     lernaCommand += cmdOptions;
-    const tagConstraint = await getNodeConstraint(config);
     const extraEnv: ExtraEnv = {
       NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE,
       npm_config_store: env.npm_config_store,
@@ -86,11 +92,9 @@ export async function generateLockFiles(
       cwdFile: upath.join(lockFileDir, 'package.json'),
       extraEnv,
       docker: {
-        image: 'node',
-        tagScheme: 'node',
-        tagConstraint,
+        image: 'sidecar',
       },
-      preCommands,
+      toolConstraints,
     };
     // istanbul ignore if
     if (GlobalConfig.get('exposeAllEnv')) {
@@ -99,7 +103,7 @@ export async function generateLockFiles(
     }
     const lernaVersion = getLernaVersion(lernaPackageFile);
     logger.debug('Using lerna version ' + lernaVersion);
-    preCommands.push(`npm i -g lerna@${quote(lernaVersion)}`);
+    toolConstraints.push({ toolName: 'lerna', constraint: lernaVersion });
     cmd.push('lerna info || echo "Ignoring lerna info failure"');
     cmd.push(`${lernaClient} install ${cmdOptions}`);
     cmd.push(lernaCommand);
diff --git a/lib/modules/manager/npm/post-update/yarn.ts b/lib/modules/manager/npm/post-update/yarn.ts
index e5a819d44f1054cf5b58bfe5eaa24ec7ce09fa6a..f46075e1257b500a21da1755c06b9f4f9faac226 100644
--- a/lib/modules/manager/npm/post-update/yarn.ts
+++ b/lib/modules/manager/npm/post-update/yarn.ts
@@ -74,9 +74,7 @@ export async function checkYarnrc(
   return { offlineMirror, yarnPath };
 }
 
-export function getOptimizeCommand(
-  fileName = '/home/ubuntu/.npm-global/lib/node_modules/yarn/lib/cli.js'
-): string {
+export function getOptimizeCommand(fileName: string): string {
   return `sed -i 's/ steps,/ steps.slice(0,1),/' ${quote(fileName)}`;
 }
 
diff --git a/lib/util/exec/buildpack.ts b/lib/util/exec/buildpack.ts
index 3224d65800f21d63fa006f36a7fb79dea46f7411..247bd49c10eaeb38658b92a3a8814b1cc27d9cc1 100644
--- a/lib/util/exec/buildpack.ts
+++ b/lib/util/exec/buildpack.ts
@@ -63,6 +63,11 @@ const allToolConfig: Record<string, ToolConfig> = {
     depName: 'jsonnet-bundler/jsonnet-bundler',
     versioning: semverVersioningId,
   },
+  lerna: {
+    datasource: 'npm',
+    depName: 'lerna',
+    versioning: npmVersioningId,
+  },
   node: {
     datasource: 'node',
     depName: 'node',