diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index 5283cde1724deba8cff24c468734c65941e06bc3..53232f4c4b453f8f0f676e870adae2a6d9686b8e 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -2889,18 +2889,27 @@ It is also recommended to avoid `rebaseWhen=never` as it can result in conflicte
 
 Avoid setting `rebaseWhen=never` and then also setting `prCreation=not-pending` as this can prevent creation of PRs.
 
-## recreateClosed
+## recreateWhen
 
-By default, Renovate will detect if it has proposed an update to a project before and not propose the same one again.
-For example the Webpack 3.x case described above.
-This field lets you customize this behavior down to a per-package level.
-For example we override it to `true` in the following cases where branch names and PR titles need to be reused:
+This feature used to be called `recreateClosed`.
+
+By default, Renovate detects if it proposed an update to a project before, and will not propose the same update again.
+For example the Webpack 3.x case described in the [`separateMajorMinor`](#separatemajorminor) documentation.
+You can use `recreateWhen` to customize this behavior down to a per-package level.
+For example we override it to `always` in the following cases where branch names and PR titles must be reused:
 
 - Package groups
 - When pinning versions
 - Lock file maintenance
 
-Typically you shouldn't need to modify this setting.
+You can select which behavior you want from Renovate:
+
+- `always`: Recreates all closed or blocking PRs
+- `auto`: The default option. Recreates only immortal PRs (default)
+- `never`: No PR is recreated, doesn't matter if it is immortal or not
+
+We recommend that you stick with the default setting for this option.
+Only change this setting if you really need to.
 
 ## regexManagers
 
diff --git a/docs/usage/key-concepts/pull-requests.md b/docs/usage/key-concepts/pull-requests.md
index 4f63045a5f7765a82822f22d0cbefb5519d0c62b..7e2af2e96a9bb1941d3ecdf2d3bda957e404b5e5 100644
--- a/docs/usage/key-concepts/pull-requests.md
+++ b/docs/usage/key-concepts/pull-requests.md
@@ -76,6 +76,7 @@ If you regularly wish to close immortal PRs, it's an indication that you may be
 ### How to fix immortal PRs
 
 Avoid grouping dependencies together which have different versions, or which you have a high chance of wanting to ignore.
+If you have immortal PRs which you want to keep closed, then set `"recreateWhen": "never"`.
 
 #### Major updates require Dependency Dashboard approval
 
diff --git a/lib/config/__snapshots__/index.spec.ts.snap b/lib/config/__snapshots__/index.spec.ts.snap
index 83825d9ee31c87852e6ae2fd6372e6fb5f271bbe..e8b55b377a356191d74cc9cf6eb149f80e6b4be2 100644
--- a/lib/config/__snapshots__/index.spec.ts.snap
+++ b/lib/config/__snapshots__/index.spec.ts.snap
@@ -12,7 +12,7 @@ exports[`config/index mergeChildConfig(parentConfig, childConfig) merges 1`] = `
     "Change": "All locks refreshed",
   },
   "rebaseStalePrs": true,
-  "recreateClosed": true,
+  "recreateWhen": "always",
   "schedule": [
     "on monday",
   ],
diff --git a/lib/config/migrations/custom/recreate-closed-migration.spec.ts b/lib/config/migrations/custom/recreate-closed-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..38628ca6c44f1f1890778fc71719178ead29a0ec
--- /dev/null
+++ b/lib/config/migrations/custom/recreate-closed-migration.spec.ts
@@ -0,0 +1,25 @@
+import { RecreateClosedMigration } from './recreate-closed-migration';
+
+describe('config/migrations/custom/recreate-closed-migration', () => {
+  it('should migrate true', () => {
+    expect(RecreateClosedMigration).toMigrate(
+      {
+        recreateClosed: true,
+      },
+      {
+        recreateWhen: 'always',
+      }
+    );
+  });
+
+  it('should migrate false', () => {
+    expect(RecreateClosedMigration).toMigrate(
+      {
+        recreateClosed: false,
+      },
+      {
+        recreateWhen: 'auto',
+      }
+    );
+  });
+});
diff --git a/lib/config/migrations/custom/recreate-closed-migration.ts b/lib/config/migrations/custom/recreate-closed-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..81cd106f4e4ee2d5dfaea13686a2a6befacebe67
--- /dev/null
+++ b/lib/config/migrations/custom/recreate-closed-migration.ts
@@ -0,0 +1,13 @@
+import is from '@sindresorhus/is';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class RecreateClosedMigration extends AbstractMigration {
+  override readonly deprecated = true;
+  override readonly propertyName = 'recreateClosed';
+
+  override run(value: unknown): void {
+    if (is.boolean(value)) {
+      this.setSafely('recreateWhen', value ? 'always' : 'auto');
+    }
+  }
+}
diff --git a/lib/config/migrations/migrations-service.ts b/lib/config/migrations/migrations-service.ts
index 1b6c0d357d515fb6c6fd6337bbc5fa9a7e8738ec..e7e3b314bb51d1debc2d0d08d76ffddc9f51f289 100644
--- a/lib/config/migrations/migrations-service.ts
+++ b/lib/config/migrations/migrations-service.ts
@@ -39,6 +39,7 @@ import { PostUpdateOptionsMigration } from './custom/post-update-options-migrati
 import { RaiseDeprecationWarningsMigration } from './custom/raise-deprecation-warnings-migration';
 import { RebaseConflictedPrs } from './custom/rebase-conflicted-prs-migration';
 import { RebaseStalePrsMigration } from './custom/rebase-stale-prs-migration';
+import { RecreateClosedMigration } from './custom/recreate-closed-migration';
 import { RenovateForkMigration } from './custom/renovate-fork-migration';
 import { RequireConfigMigration } from './custom/require-config-migration';
 import { RequiredStatusChecksMigration } from './custom/required-status-checks-migration';
@@ -145,6 +146,7 @@ export class MigrationsService {
     SemanticPrefixMigration,
     MatchDatasourcesMigration,
     DatasourceMigration,
+    RecreateClosedMigration,
     StabilityDaysMigration,
   ];
 
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index f71066be46c06b4e363af7d43b73e5279e9a3e16..f539bbf96b839a4e0ad4ac6914b1fd78a9bf2c46 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -1574,10 +1574,11 @@ const options: RenovateOptions[] = [
     default: false,
   },
   {
-    name: 'recreateClosed',
+    name: 'recreateWhen',
     description: 'Recreate PRs even if same ones were closed previously.',
-    type: 'boolean',
-    default: false,
+    type: 'string',
+    default: 'auto',
+    allowedValues: ['auto', 'always', 'never'],
   },
   {
     name: 'rebaseWhen',
@@ -1899,7 +1900,7 @@ const options: RenovateOptions[] = [
     type: 'object',
     default: {
       enabled: false,
-      recreateClosed: true,
+      recreateWhen: 'always',
       rebaseStalePrs: true,
       branchTopic: 'lock-file-maintenance',
       commitMessageAction: 'Lock file maintenance',
diff --git a/lib/config/types.ts b/lib/config/types.ts
index 5c390b09c825a03ca10fd39511dbc218c8b4ba6b..ac915cd80304f56a9a80a6aa68928104f396fe8d 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -21,6 +21,7 @@ export interface GroupConfig extends Record<string, unknown> {
   branchTopic?: string;
 }
 
+export type RecreateWhen = 'auto' | 'never' | 'always';
 // TODO: Proper typings
 export interface RenovateSharedConfig {
   $schema?: string;
@@ -70,6 +71,7 @@ export interface RenovateSharedConfig {
   respectLatest?: boolean;
   stopUpdatingLabel?: string;
   rebaseWhen?: string;
+  recreateWhen?: RecreateWhen;
   recreateClosed?: boolean;
   repository?: string;
   repositoryCache?: RepositoryCacheConfig;
diff --git a/lib/workers/global/config/parse/cli.spec.ts b/lib/workers/global/config/parse/cli.spec.ts
index a98b4f89390d20ba61556c7ab195a6b02a9ed93d..8cb7a3d7851a9d01dd2445843a7056105f681d03 100644
--- a/lib/workers/global/config/parse/cli.spec.ts
+++ b/lib/workers/global/config/parse/cli.spec.ts
@@ -33,19 +33,19 @@ describe('workers/global/config/parse/cli', () => {
     });
 
     it('supports boolean no value', () => {
-      argv.push('--recreate-closed');
-      expect(cli.getConfig(argv)).toEqual({ recreateClosed: true });
+      argv.push('--config-migration');
+      expect(cli.getConfig(argv)).toEqual({ configMigration: true });
       argv = argv.slice(0, -1);
     });
 
     it('supports boolean space true', () => {
-      argv.push('--recreate-closed');
+      argv.push('--config-migration');
       argv.push('true');
-      expect(cli.getConfig(argv)).toEqual({ recreateClosed: true });
+      expect(cli.getConfig(argv)).toEqual({ configMigration: true });
     });
 
     it('throws exception for invalid boolean value', () => {
-      argv.push('--recreate-closed');
+      argv.push('--config-migration');
       argv.push('badvalue');
       expect(() => cli.getConfig(argv)).toThrow(
         Error(
@@ -55,19 +55,19 @@ describe('workers/global/config/parse/cli', () => {
     });
 
     it('supports boolean space false', () => {
-      argv.push('--recreate-closed');
+      argv.push('--config-migration');
       argv.push('false');
-      expect(cli.getConfig(argv)).toEqual({ recreateClosed: false });
+      expect(cli.getConfig(argv)).toEqual({ configMigration: false });
     });
 
     it('supports boolean equals true', () => {
-      argv.push('--recreate-closed=true');
-      expect(cli.getConfig(argv)).toEqual({ recreateClosed: true });
+      argv.push('--config-migration=true');
+      expect(cli.getConfig(argv)).toEqual({ configMigration: true });
     });
 
     it('supports boolean equals false', () => {
-      argv.push('--recreate-closed=false');
-      expect(cli.getConfig(argv)).toEqual({ recreateClosed: false });
+      argv.push('--config-migration=false');
+      expect(cli.getConfig(argv)).toEqual({ configMigration: false });
     });
 
     it('supports list single', () => {
@@ -130,6 +130,12 @@ describe('workers/global/config/parse/cli', () => {
       ${'--git-lab-automerge=false'}   | ${{ platformAutomerge: false }}
       ${'--git-lab-automerge=true'}    | ${{ platformAutomerge: true }}
       ${'--git-lab-automerge'}         | ${{ platformAutomerge: true }}
+      ${'--recreate-closed=false'}     | ${{ recreateWhen: 'auto' }}
+      ${'--recreate-closed=true'}      | ${{ recreateWhen: 'always' }}
+      ${'--recreate-closed'}           | ${{ recreateWhen: 'always' }}
+      ${'--recreate-when=auto'}        | ${{ recreateWhen: 'auto' }}
+      ${'--recreate-when=always'}      | ${{ recreateWhen: 'always' }}
+      ${'--recreate-when=never'}       | ${{ recreateWhen: 'never' }}
     `('"$arg" -> $config', ({ arg, config }) => {
       argv.push(arg);
       expect(cli.getConfig(argv)).toMatchObject(config);
diff --git a/lib/workers/global/config/parse/cli.ts b/lib/workers/global/config/parse/cli.ts
index f12305fa3e28ab6ae867a91fe843ae87ee6f72b0..37c8d37cac19c95c4b159acbfaf8b89f915f82bf 100644
--- a/lib/workers/global/config/parse/cli.ts
+++ b/lib/workers/global/config/parse/cli.ts
@@ -34,6 +34,9 @@ export function getConfig(input: string[]): AllConfig {
         .replace('--aliases', '--registry-aliases')
         .replace('--include-forks=true', '--fork-processing=enabled')
         .replace('--include-forks', '--fork-processing=enabled')
+        .replace('--recreate-closed=false', '--recreate-when=auto')
+        .replace('--recreate-closed=true', '--recreate-when=always')
+        .replace('--recreate-closed', '--recreate-when=always')
     )
     .filter((a) => !a.startsWith('--git-fs'));
   const options = getOptions();
diff --git a/lib/workers/global/config/parse/env.spec.ts b/lib/workers/global/config/parse/env.spec.ts
index ff963b7658dadc5f9cad660e409bc5641af46c1b..672839f1c31a60c275ee509d77ce1a2d9c2ac69b 100644
--- a/lib/workers/global/config/parse/env.spec.ts
+++ b/lib/workers/global/config/parse/env.spec.ts
@@ -10,18 +10,20 @@ describe('workers/global/config/parse/env', () => {
     });
 
     it('supports boolean true', () => {
-      const envParam: NodeJS.ProcessEnv = { RENOVATE_RECREATE_CLOSED: 'true' };
-      expect(env.getConfig(envParam).recreateClosed).toBeTrue();
+      const envParam: NodeJS.ProcessEnv = { RENOVATE_CONFIG_MIGRATION: 'true' };
+      expect(env.getConfig(envParam).configMigration).toBeTrue();
     });
 
     it('supports boolean false', () => {
-      const envParam: NodeJS.ProcessEnv = { RENOVATE_RECREATE_CLOSED: 'false' };
-      expect(env.getConfig(envParam).recreateClosed).toBeFalse();
+      const envParam: NodeJS.ProcessEnv = {
+        RENOVATE_CONFIG_MIGRATION: 'false',
+      };
+      expect(env.getConfig(envParam).configMigration).toBeFalse();
     });
 
     it('throws exception for invalid boolean value', () => {
       const envParam: NodeJS.ProcessEnv = {
-        RENOVATE_RECREATE_CLOSED: 'badvalue',
+        RENOVATE_CONFIG_MIGRATION: 'badvalue',
       };
       expect(() => env.getConfig(envParam)).toThrow(
         Error(
@@ -30,7 +32,7 @@ describe('workers/global/config/parse/env', () => {
       );
     });
 
-    delete process.env.RENOVATE_RECREATE_CLOSED;
+    delete process.env.RENOVATE_CONFIG_MIGRATION;
 
     it('supports list single', () => {
       const envParam: NodeJS.ProcessEnv = { RENOVATE_LABELS: 'a' };
@@ -83,6 +85,17 @@ describe('workers/global/config/parse/env', () => {
       expect(res).toMatchObject({ hostRules: [{ foo: 'bar' }] });
     });
 
+    test.each`
+      envArg                                   | config
+      ${{ RENOVATE_RECREATE_CLOSED: 'true' }}  | ${{ recreateWhen: 'always' }}
+      ${{ RENOVATE_RECREATE_CLOSED: 'false' }} | ${{ recreateWhen: 'auto' }}
+      ${{ RENOVATE_RECREATE_WHEN: 'auto' }}    | ${{ recreateWhen: 'auto' }}
+      ${{ RENOVATE_RECREATE_WHEN: 'always' }}  | ${{ recreateWhen: 'always' }}
+      ${{ RENOVATE_RECREATE_WHEN: 'never' }}   | ${{ recreateWhen: 'never' }}
+    `('"$envArg" -> $config', ({ envArg, config }) => {
+      expect(env.getConfig(envArg)).toMatchObject(config);
+    });
+
     it('skips misconfigured arrays', () => {
       const envName = 'RENOVATE_HOST_RULES';
       const val = JSON.stringify('foobar');
diff --git a/lib/workers/global/config/parse/env.ts b/lib/workers/global/config/parse/env.ts
index 4c1afc8b7ee446795b8b46093beb9d9825e7d590..f176043f0854b34e2cc446817c913272e4608321 100644
--- a/lib/workers/global/config/parse/env.ts
+++ b/lib/workers/global/config/parse/env.ts
@@ -53,10 +53,41 @@ function renameEnvKeys(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
   return result;
 }
 
+const migratedKeysWithValues = [
+  {
+    oldName: 'recreateClosed',
+    newName: 'recreateWhen',
+    from: 'true',
+    to: 'always',
+  },
+  {
+    oldName: 'recreateClosed',
+    newName: 'recreateWhen',
+    from: 'false',
+    to: 'auto',
+  },
+];
+
+function massageEnvKeyValues(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
+  const result = { ...env };
+  for (const { oldName, newName, from, to } of migratedKeysWithValues) {
+    const key = getEnvName({ name: oldName });
+    if (env[key] !== undefined) {
+      if (result[key] === from) {
+        delete result[key];
+        result[getEnvName({ name: newName })] = to;
+      }
+    }
+  }
+  return result;
+}
+
 export function getConfig(inputEnv: NodeJS.ProcessEnv): AllConfig {
   let env = inputEnv;
   env = normalizePrefixes(inputEnv, inputEnv.ENV_PREFIX);
   env = renameEnvKeys(env);
+  // massage the values of migrated configuration keys
+  env = massageEnvKeyValues(env);
 
   const options = getOptions();
 
diff --git a/lib/workers/repository/update/branch/check-existing.spec.ts b/lib/workers/repository/update/branch/check-existing.spec.ts
index ff7c545d85a5708398be0a269f4020edd5751315..c1f219f22aea3944162f7aab68a0ab35f5f656a5 100644
--- a/lib/workers/repository/update/branch/check-existing.spec.ts
+++ b/lib/workers/repository/update/branch/check-existing.spec.ts
@@ -20,13 +20,13 @@ describe('workers/repository/update/branch/check-existing', () => {
     });
 
     it('returns false if recreating closed PRs', async () => {
-      config.recreateClosed = true;
+      config.recreateWhen = 'always';
       expect(await prAlreadyExisted(config)).toBeNull();
       expect(platform.findPr).toHaveBeenCalledTimes(0);
     });
 
     it('returns false if check misses', async () => {
-      config.recreatedClosed = true;
+      config.recreateWhen = 'auto';
       expect(await prAlreadyExisted(config)).toBeNull();
       expect(platform.findPr).toHaveBeenCalledTimes(1);
     });
diff --git a/lib/workers/repository/update/branch/check-existing.ts b/lib/workers/repository/update/branch/check-existing.ts
index 877d28fa0d378fb5e9a42a606d16a6b669de594f..c0fdba11548862995ff25531e86ca1d4ee313c9b 100644
--- a/lib/workers/repository/update/branch/check-existing.ts
+++ b/lib/workers/repository/update/branch/check-existing.ts
@@ -8,11 +8,13 @@ export async function prAlreadyExisted(
   config: BranchConfig
 ): Promise<Pr | null> {
   logger.trace({ config }, 'prAlreadyExisted');
-  if (config.recreateClosed) {
-    logger.debug('recreateClosed is true');
+  if (config.recreateWhen === 'always') {
+    logger.debug('recreateWhen is "always". No need to check for closed PR.');
     return null;
   }
-  logger.debug('recreateClosed is false');
+  logger.debug(
+    'Check for closed PR because recreating closed PRs is disabled.'
+  );
   // Return if same PR already existed
   let pr = await platform.findPr({
     branchName: config.branchName,
diff --git a/lib/workers/repository/update/branch/index.spec.ts b/lib/workers/repository/update/branch/index.spec.ts
index 66e276a240c3afdedfac19565de47c019e03c20e..4a438e25442d8668a9313e6e252c1f470ff0c3eb 100644
--- a/lib/workers/repository/update/branch/index.spec.ts
+++ b/lib/workers/repository/update/branch/index.spec.ts
@@ -1026,7 +1026,7 @@ describe('workers/repository/update/branch/index', () => {
         artifactErrors: [partial<ArtifactError>()],
         updatedArtifacts: [partial<FileChange>()],
       });
-      config.recreateClosed = true;
+      config.recreateWhen = 'always';
       scm.branchExists.mockResolvedValue(true);
       automerge.tryBranchAutomerge.mockResolvedValueOnce('failed');
       prWorker.ensurePr.mockResolvedValueOnce({
diff --git a/lib/workers/repository/update/pr/body/config-description.spec.ts b/lib/workers/repository/update/pr/body/config-description.spec.ts
index 435c37258c3940c3fa40b87324c12d8e0b3734a8..37bba58b2cc95b504ea798da3f5af9fefb50da52 100644
--- a/lib/workers/repository/update/pr/body/config-description.spec.ts
+++ b/lib/workers/repository/update/pr/body/config-description.spec.ts
@@ -67,7 +67,7 @@ describe('workers/repository/update/pr/body/config-description', () => {
       expect(res).toContain(`At any time (no schedule defined).`);
     });
 
-    it('renders recreateClosed', () => {
+    it('renders recreateClosed=true', () => {
       const res = getPrConfigDescription({
         ...config,
         recreateClosed: true,
@@ -75,6 +75,19 @@ describe('workers/repository/update/pr/body/config-description', () => {
       expect(res).toContain(`**Immortal**`);
     });
 
+    it('does not render recreateClosed=false', () => {
+      const res = getPrConfigDescription({
+        ...config,
+        recreateClosed: false,
+      });
+      expect(res).not.toContain(`**Immortal**`);
+    });
+
+    it('does not render recreateClosed=undefined', () => {
+      const res = getPrConfigDescription(config);
+      expect(res).not.toContain(`**Immortal**`);
+    });
+
     it('renders singular', () => {
       const res = getPrConfigDescription({
         ...config,
diff --git a/lib/workers/repository/update/pr/pr-fingerprint.ts b/lib/workers/repository/update/pr/pr-fingerprint.ts
index 955852278123a8d4a1002f2ddd3c3151890469d3..cd91d440690b5b89d6f725210e7df826b41958fc 100644
--- a/lib/workers/repository/update/pr/pr-fingerprint.ts
+++ b/lib/workers/repository/update/pr/pr-fingerprint.ts
@@ -1,7 +1,11 @@
 // fingerprint config is based on the old skip pr update logic
 // https://github.com/renovatebot/renovate/blob/3d85b6048d6a8c57887b64ed4929e2e02ea41aa0/lib/workers/repository/update/pr/index.ts#L294-L306
 
-import type { UpdateType, ValidationMessage } from '../../../../config/types';
+import type {
+  RecreateWhen,
+  UpdateType,
+  ValidationMessage,
+} from '../../../../config/types';
 import { logger } from '../../../../logger';
 import type { PrCache } from '../../../../util/cache/repository/types';
 import { getElapsedHours } from '../../../../util/date';
@@ -28,7 +32,7 @@ export interface PrBodyFingerprintConfig {
   prHeader?: string;
   prTitle?: string;
   rebaseWhen?: string;
-  recreateClosed?: boolean;
+  recreateWhen?: RecreateWhen;
   schedule?: string[];
   stopUpdating?: boolean;
   timezone?: string;
@@ -67,7 +71,7 @@ export function generatePrBodyFingerprintConfig(
     prHeader: config.prHeader,
     prTitle: config.prTitle,
     rebaseWhen: config.rebaseWhen,
-    recreateClosed: config.recreateClosed,
+    recreateWhen: config.recreateWhen,
     schedule: config.schedule,
     stopUpdating: config.stopUpdating,
     timezone: config.timezone,
diff --git a/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap b/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap
index 9455b1b87aa6b6053fb86495b95fd4330b432367..693e02a74fbe5f514cb69aa2c743ffe741e8ac08 100644
--- a/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap
+++ b/lib/workers/repository/updates/__snapshots__/generate.spec.ts.snap
@@ -178,6 +178,7 @@ exports[`workers/repository/updates/generate generateBranchConfig() handles lock
   "prBodyColumns": [],
   "prTitle": "some-title",
   "prettyDepType": "dependency",
+  "recreateClosed": true,
   "releaseTimestamp": undefined,
   "reuseLockFiles": true,
   "upgrades": [
@@ -191,6 +192,7 @@ exports[`workers/repository/updates/generate generateBranchConfig() handles lock
       "manager": "some-manager",
       "prTitle": "some-title",
       "prettyDepType": "dependency",
+      "recreateClosed": true,
     },
   ],
 }
diff --git a/lib/workers/repository/updates/generate.spec.ts b/lib/workers/repository/updates/generate.spec.ts
index c44d2e74ee6cf5263f22ae1ca6edb18900e82ab8..ca0b111abb5bd8bb89b10eb9578064769302cf8c 100644
--- a/lib/workers/repository/updates/generate.spec.ts
+++ b/lib/workers/repository/updates/generate.spec.ts
@@ -67,6 +67,7 @@ describe('workers/repository/updates/generate', () => {
             branchName: 'some-branch',
             prTitle: 'some-title',
             isLockFileMaintenance: true,
+            recreateClosed: true,
           },
         ],
       });
@@ -216,7 +217,7 @@ describe('workers/repository/updates/generate', () => {
       });
     });
 
-    it('groups major updates with different versions but same newValue, no recreateClosed', () => {
+    it('groups major updates with different versions but same newValue, no recreateWhen', () => {
       const branch = [
         {
           manager: 'some-manager',
@@ -282,7 +283,7 @@ describe('workers/repository/updates/generate', () => {
       expect(res.recreateClosed).toBeTrue();
     });
 
-    it('Grouped pin & pinDigest can be recreated', () => {
+    it('recreates grouped pin & pinDigest', () => {
       const branch = [
         {
           ...requiredDefaultOptions,
@@ -304,7 +305,31 @@ describe('workers/repository/updates/generate', () => {
       expect(res.recreateClosed).toBeTrue();
     });
 
-    it('Grouped pin can be recreated', () => {
+    it('does not recreate grouped pin & pinDigest when closed if recreateWhen=never', () => {
+      const branch = [
+        {
+          ...requiredDefaultOptions,
+          isPinDigest: true,
+          updateType: 'pinDigest',
+          newValue: 'v2',
+          newDigest: 'dc323e67f16fb5f7663d20ff7941f27f5809e9b6',
+          recreateWhen: 'never',
+        },
+        {
+          ...requiredDefaultOptions,
+          updateType: 'pin',
+          isPin: true,
+          newValue: "'2.2.0'",
+          newVersion: '2.2.0',
+          newMajor: 2,
+          recreateWhen: 'never',
+        },
+      ] as BranchUpgradeConfig[];
+      const res = generateBranchConfig(branch);
+      expect(res.recreateClosed).toBeFalse();
+    });
+
+    it('recreates grouped pin', () => {
       const branch = [
         {
           ...requiredDefaultOptions,
@@ -331,7 +356,7 @@ describe('workers/repository/updates/generate', () => {
       expect(res.recreateClosed).toBeTrue();
     });
 
-    it('grouped pinDigest can be recreated', () => {
+    it('recreates grouped pinDigest', () => {
       const branch = [
         {
           ...requiredDefaultOptions,
diff --git a/lib/workers/repository/updates/generate.ts b/lib/workers/repository/updates/generate.ts
index c56cb1434567d6f4b76fc4afe500e5fc44789d23..c044fb94d175753d441783f4a04bcdbb71c73aa3 100644
--- a/lib/workers/repository/updates/generate.ts
+++ b/lib/workers/repository/updates/generate.ts
@@ -103,6 +103,10 @@ export function generateBranchConfig(
       upg.displayFrom = upg.currentValue;
       upg.displayTo = upg.newValue;
     }
+
+    if (upg.isLockFileMaintenance) {
+      upg.recreateClosed = upg.recreateWhen !== 'never';
+    }
     upg.displayFrom ??= '';
     upg.displayTo ??= '';
     if (!depNames.includes(upg.depName!)) {
@@ -178,14 +182,14 @@ export function generateBranchConfig(
       logger.trace({ toVersions });
       logger.trace({ toValues });
       delete upgrade.commitMessageExtra;
-      upgrade.recreateClosed = true;
+      upgrade.recreateClosed = upgrade.recreateWhen !== 'never';
     } else if (
       newValue.length > 1 &&
       (upgrade.isDigest || upgrade.isPinDigest)
     ) {
       logger.trace({ newValue });
       delete upgrade.commitMessageExtra;
-      upgrade.recreateClosed = true;
+      upgrade.recreateClosed = upgrade.recreateWhen !== 'never';
     } else if (semver.valid(toVersions[0])) {
       upgrade.isRange = false;
     }