diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index cec8095debea21d7342e5d71e1ab3cf61e111a08..94c6e74e0c5a98b036c450a112f1f45d5dc36dff 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -1924,6 +1924,7 @@ This way Renovate can use GitHub's [Commit signing support for bots and other Gi
 
 ## postUpdateOptions
 
+- `bundlerConservative`: Enable conservative mode for `bundler` (Ruby dependencies). This will only update the immediate dependency in the lockfile instead of all subdependencies
 - `gomodMassage`: Enable massaging `replace` directives before calling `go` commands
 - `gomodTidy`: Run `go mod tidy` after Go module updates. This is implicitly enabled for major module updates when `gomodUpdateImportPaths` is enabled
 - `gomodTidy1.17`: Run `go mod tidy -compat=1.17` after Go module updates.
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 4f4920eded6bfb1d47b86c36cf73c6c480b45373..37bb30e434b1448bbea632063b42e21097c6ceff 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -1853,6 +1853,7 @@ const options: RenovateOptions[] = [
     type: 'array',
     default: [],
     allowedValues: [
+      'bundlerConservative',
       'gomodMassage',
       'gomodUpdateImportPaths',
       'gomodTidy',
diff --git a/lib/modules/manager/bundler/artifacts.spec.ts b/lib/modules/manager/bundler/artifacts.spec.ts
index 47807c32bb1b7f5ddaac36566474a069a6068744..46fe6e91a7fdb64cc57e56c2a48a0a3c2a1961bb 100644
--- a/lib/modules/manager/bundler/artifacts.spec.ts
+++ b/lib/modules/manager/bundler/artifacts.spec.ts
@@ -128,6 +128,36 @@ describe('modules/manager/bundler/artifacts', () => {
     expect(execSnapshots).toMatchSnapshot();
   });
 
+  it('supports conservative mode', async () => {
+    fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock');
+    fs.writeLocalFile.mockResolvedValueOnce();
+    fs.readLocalFile.mockResolvedValueOnce(null);
+    const execSnapshots = mockExecAll(exec);
+    git.getRepoStatus.mockResolvedValueOnce({
+      modified: ['Gemfile.lock'],
+    } as StatusResult);
+    fs.readLocalFile.mockResolvedValueOnce('Updated Gemfile.lock');
+    expect(
+      await updateArtifacts({
+        packageFileName: 'Gemfile',
+        updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
+        newPackageFileContent: 'Updated Gemfile content',
+        config: {
+          ...config,
+          postUpdateOptions: [
+            ...(config.postUpdateOptions ?? []),
+            'bundlerConservative',
+          ],
+        },
+      })
+    ).toEqual([updatedGemfileLock]);
+    expect(execSnapshots).toMatchObject([
+      expect.objectContaining({
+        cmd: 'bundler lock --conservative --update foo bar',
+      }),
+    ]);
+  });
+
   describe('Docker', () => {
     beforeEach(() => {
       GlobalConfig.set({
diff --git a/lib/modules/manager/bundler/artifacts.ts b/lib/modules/manager/bundler/artifacts.ts
index f628afb66ac1e6cb97a4bda5752f77b241ccd0a2..495260a1c5dfd3154d4d182514b6f8ecb6118db4 100644
--- a/lib/modules/manager/bundler/artifacts.ts
+++ b/lib/modules/manager/bundler/artifacts.ts
@@ -1,4 +1,5 @@
 import { lt } from '@renovatebot/ruby-semver';
+import is from '@sindresorhus/is';
 import { quote } from 'shlex';
 import {
   BUNDLER_INVALID_CREDENTIALS,
@@ -61,6 +62,12 @@ export async function updateArtifacts(
     return null;
   }
 
+  const args = [
+    config.postUpdateOptions?.includes('bundlerConservative') &&
+      '--conservative',
+    '--update',
+  ].filter(is.nonEmptyString);
+
   try {
     await writeLocalFile(packageFileName, newPackageFileContent);
 
@@ -69,7 +76,7 @@ export async function updateArtifacts(
     if (config.isLockFileMaintenance) {
       cmd = 'bundler lock --update';
     } else {
-      cmd = `bundler lock --update ${updatedDeps
+      cmd = `bundler lock ${args.join(' ')} ${updatedDeps
         .map((dep) => `${dep.depName}`)
         .filter((dep) => dep !== 'ruby')
         .map(quote)