diff --git a/lib/manager/npm/post-update/lerna.ts b/lib/manager/npm/post-update/lerna.ts
index 2cd440498d25293604e2763b52da8f0331fc7471..a7b3329cce141b37300aa6a9be692fd7311d5739 100644
--- a/lib/manager/npm/post-update/lerna.ts
+++ b/lib/manager/npm/post-update/lerna.ts
@@ -72,8 +72,7 @@ export async function generateLockFiles(
       lernaCommand = lernaCommand.replace('--ignore-scripts ', '');
     }
     lernaCommand += cmdOptions;
-    const allowUnstable = true; // lerna will pick the default installed npm@6 unless we use node@>=15
-    const tagConstraint = await getNodeConstraint(config, allowUnstable);
+    const tagConstraint = await getNodeConstraint(config);
     const execOptions: ExecOptions = {
       cwd,
       extraEnv: {
@@ -82,7 +81,7 @@ export async function generateLockFiles(
       },
       docker: {
         image: 'node',
-        tagScheme: 'npm',
+        tagScheme: 'node',
         tagConstraint,
         preCommands,
       },
diff --git a/lib/manager/npm/post-update/node-version.spec.ts b/lib/manager/npm/post-update/node-version.spec.ts
index 617ce7d9886a23262ce914dae04bf89ab73fa6ac..c32f94d27116d887e627f3276ce0912e58550251 100644
--- a/lib/manager/npm/post-update/node-version.spec.ts
+++ b/lib/manager/npm/post-update/node-version.spec.ts
@@ -1,5 +1,4 @@
 import { fs } from '../../../../test/util';
-import { isStable } from '../../../versioning/node';
 import { getNodeConstraint } from './node-version';
 
 jest.mock('../../../util/fs');
@@ -16,45 +15,6 @@ describe('manager/npm/post-update/node-version', () => {
     const res = await getNodeConstraint(config);
     expect(res).toEqual('^12.16.0');
   });
-  it('augments to avoid node 15', async () => {
-    fs.readLocalFile = jest.fn();
-    fs.readLocalFile.mockResolvedValueOnce(null);
-    fs.readLocalFile.mockResolvedValueOnce(null);
-    const res = await getNodeConstraint({
-      ...config,
-      constraints: { node: '>= 12.16.0' },
-    });
-    const isAugmentedRange = res === '>= 12.16.0 <15';
-    const node16IsStable = isStable('16.100.0');
-    expect(isAugmentedRange || node16IsStable).toBe(true);
-  });
-  it('forces node 15 if v2 lockfile detected and constraint allows', async () => {
-    fs.readLocalFile.mockResolvedValueOnce(null);
-    fs.readLocalFile.mockResolvedValueOnce(null);
-    fs.readLocalFile.mockResolvedValueOnce('{"lockfileVersion":2}');
-    const res = await getNodeConstraint({
-      ...config,
-      constraints: { node: '>= 12.16.0' },
-    });
-    const isAugmentedRange = res === '>=15 <17';
-    const node16IsStable = isStable('16.100.0');
-    expect(isAugmentedRange || node16IsStable).toBe(true);
-  });
-  it('forces node 15 if v2 lockfile detected and no constraint', async () => {
-    fs.readLocalFile.mockResolvedValueOnce(null);
-    fs.readLocalFile.mockResolvedValueOnce(null);
-    fs.readLocalFile.mockResolvedValueOnce('{"lockfileVersion":2}');
-    const res = await getNodeConstraint(
-      {
-        ...config,
-        constraints: {},
-      },
-      true
-    );
-    const isAugmentedRange = res === '>=15';
-    const node16IsStable = isStable('16.100.0');
-    expect(isAugmentedRange || node16IsStable).toBe(true);
-  });
   it('returns .node-version value', async () => {
     fs.readLocalFile = jest.fn();
     fs.readLocalFile.mockResolvedValueOnce(null);
diff --git a/lib/manager/npm/post-update/node-version.ts b/lib/manager/npm/post-update/node-version.ts
index 30d11b13851daf6c3c33005d157c9f2ad9f4bcd3..597cb4ce94061530397a87ab7ac0dfdc580867d3 100644
--- a/lib/manager/npm/post-update/node-version.ts
+++ b/lib/manager/npm/post-update/node-version.ts
@@ -1,8 +1,7 @@
-import { satisfies, validRange } from 'semver';
+import { validRange } from 'semver';
 import { logger } from '../../../logger';
 import { getSiblingFileName, readLocalFile } from '../../../util/fs';
 import { regEx } from '../../../util/regex';
-import { isStable } from '../../../versioning/node';
 import type { PostUpdateConfig } from '../../types';
 
 async function getNodeFile(filename: string): Promise<string> | null {
@@ -30,44 +29,14 @@ function getPackageJsonConstraint(config: PostUpdateConfig): string | null {
 }
 
 export async function getNodeConstraint(
-  config: PostUpdateConfig,
-  allowUnstable = false
+  config: PostUpdateConfig
 ): Promise<string> | null {
   const { packageFile } = config;
-  let constraint =
+  const constraint =
     (await getNodeFile(getSiblingFileName(packageFile, '.nvmrc'))) ||
     (await getNodeFile(getSiblingFileName(packageFile, '.node-version'))) ||
     getPackageJsonConstraint(config);
-  let lockfileVersion = 1;
-  try {
-    const lockFileName = getSiblingFileName(packageFile, 'package-lock.json');
-    lockfileVersion = JSON.parse(
-      await readLocalFile(lockFileName, 'utf8')
-    ).lockfileVersion;
-  } catch (err) {
-    // do nothing
-  }
-  // Avoid using node 15 if node 14 also satisfies the same constraint
-  // Remove this once node 16 is LTS
-  if (constraint) {
-    if (
-      validRange(constraint) &&
-      satisfies('14.100.0', constraint) &&
-      satisfies('15.100.0', constraint) &&
-      !isStable('16.100.0')
-    ) {
-      if (lockfileVersion === 2) {
-        logger.debug('Forcing node 15+ to ensure lockfileVersion=2 is used');
-        constraint = '>=15 <17';
-      } else if (validRange(`${constraint} <15`)) {
-        logger.debug('Augmenting constraint to avoid node 15');
-        constraint = `${constraint} <15`;
-      }
-    }
-  } else if (allowUnstable && lockfileVersion === 2) {
-    logger.debug('Using node >=15 for lockfileVersion=2');
-    constraint = '>=15';
-  } else {
+  if (!constraint) {
     logger.debug('No node constraint found - using latest');
   }
   return constraint;
diff --git a/lib/manager/npm/post-update/npm.ts b/lib/manager/npm/post-update/npm.ts
index b94e424015cb2d89ad7e86a0360818c03ca3637a..c31770c815b7ae81c88aee27564ec69c1816788a 100644
--- a/lib/manager/npm/post-update/npm.ts
+++ b/lib/manager/npm/post-update/npm.ts
@@ -65,7 +65,7 @@ export async function generateLockFile(
       },
       docker: {
         image: 'node',
-        tagScheme: 'npm',
+        tagScheme: 'node',
         tagConstraint,
         preCommands,
       },
diff --git a/lib/manager/npm/post-update/pnpm.ts b/lib/manager/npm/post-update/pnpm.ts
index 2317d55c6aab15966aa8c4b43d96c3c597103abd..d2fe217858462460d5a4fb4f9256b0cbdcee948b 100644
--- a/lib/manager/npm/post-update/pnpm.ts
+++ b/lib/manager/npm/post-update/pnpm.ts
@@ -38,7 +38,7 @@ export async function generateLockFile(
       },
       docker: {
         image: 'node',
-        tagScheme: 'npm',
+        tagScheme: 'node',
         tagConstraint,
         preCommands,
       },
diff --git a/lib/manager/npm/post-update/yarn.ts b/lib/manager/npm/post-update/yarn.ts
index 1ca1c9523377c686bbaf0afd133b4c23961ff7da..d6a517b1a3e5a0ff4db8922b47891da3dfbd3329 100644
--- a/lib/manager/npm/post-update/yarn.ts
+++ b/lib/manager/npm/post-update/yarn.ts
@@ -129,7 +129,7 @@ export async function generateLockFile(
       extraEnv,
       docker: {
         image: 'node',
-        tagScheme: 'npm',
+        tagScheme: 'node',
         tagConstraint,
         preCommands,
       },
diff --git a/lib/util/exec/docker/index.spec.ts b/lib/util/exec/docker/index.spec.ts
index 5c8f35b1f0c96d9c32fa130be915b4caccc2f9f4..4c8d4dea7dfcd70be2260be4132ce4285f1bb176 100644
--- a/lib/util/exec/docker/index.spec.ts
+++ b/lib/util/exec/docker/index.spec.ts
@@ -95,6 +95,16 @@ describe('util/exec/docker/index', () => {
       getPkgReleases.mockResolvedValueOnce({ releases } as never);
       expect(await getDockerTag('foo', '^1.2.3', 'npm')).toBe('1.9.9');
     });
+    it('filters out node unstable', async () => {
+      const releases = [
+        { version: '12.0.0' },
+        { version: '13.0.1' },
+        { version: '14.0.2' },
+        { version: '15.0.2' },
+      ];
+      getPkgReleases.mockResolvedValueOnce({ releases } as never);
+      expect(await getDockerTag('foo', '>=12', 'node')).toBe('14.0.2');
+    });
   });
 
   describe('removeDockerContainer', () => {
diff --git a/lib/util/exec/docker/index.ts b/lib/util/exec/docker/index.ts
index 0cecae59053b4280790ee993c00c1c9a1eb9b1f7..a6a160653dce6e5ea265b683b3405f3656b6e4fc 100644
--- a/lib/util/exec/docker/index.ts
+++ b/lib/util/exec/docker/index.ts
@@ -93,6 +93,11 @@ export async function getDockerTag(
     versions = versions.filter(
       (version) => ver.isVersion(version) && ver.matches(version, constraint)
     );
+    // Prefer stable versions over unstable, even if the range satisfies both types
+    if (!versions.every((version) => ver.isStable(version))) {
+      logger.debug('Filtering out unstable versions');
+      versions = versions.filter((version) => ver.isStable(version));
+    }
     versions = versions.sort(ver.sortVersions.bind(ver));
     if (versions.length) {
       const version = versions.pop();