diff --git a/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap b/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap
index 52534d46a332fbe9ccc13aa5931cf60af70f9e47..749d49e4010a6055d4fff2b7b288adf0c62b23fc 100644
--- a/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap
+++ b/lib/manager/bundler/__snapshots__/artifacts.spec.ts.snap
@@ -181,6 +181,28 @@ Array [
 ]
 `;
 
+exports[`bundler.updateArtifacts() performs lockFileMaintenance 1`] = `
+Array [
+  Object {
+    "cmd": "bundle lock",
+    "options": Object {
+      "cwd": "/tmp/github/some/repo",
+      "encoding": "utf-8",
+      "env": Object {
+        "HOME": "/home/user",
+        "HTTPS_PROXY": "https://example.com",
+        "HTTP_PROXY": "http://example.com",
+        "LANG": "en_US.UTF-8",
+        "LC_ALL": "en_US",
+        "NO_PROXY": "localhost",
+        "PATH": "/tmp/path",
+      },
+      "timeout": 900000,
+    },
+  },
+]
+`;
+
 exports[`bundler.updateArtifacts() returns null if Gemfile.lock was not changed 1`] = `null`;
 
 exports[`bundler.updateArtifacts() returns null if Gemfile.lock was not changed 2`] = `
@@ -205,6 +227,28 @@ Array [
 ]
 `;
 
+exports[`bundler.updateArtifacts() throws BUNDLER_UNKNOWN_ERROR when failing in lockFileMaintenance true 1`] = `
+Array [
+  Object {
+    "cmd": "bundle lock",
+    "options": Object {
+      "cwd": "/tmp/github/some/repo",
+      "encoding": "utf-8",
+      "env": Object {
+        "HOME": "/home/user",
+        "HTTPS_PROXY": "https://example.com",
+        "HTTP_PROXY": "http://example.com",
+        "LANG": "en_US.UTF-8",
+        "LC_ALL": "en_US",
+        "NO_PROXY": "localhost",
+        "PATH": "/tmp/path",
+      },
+      "timeout": 900000,
+    },
+  },
+]
+`;
+
 exports[`bundler.updateArtifacts() works explicit global binarySource 1`] = `
 Array [
   Object {
diff --git a/lib/manager/bundler/artifacts.spec.ts b/lib/manager/bundler/artifacts.spec.ts
index e7043503e0cb81b97923785245c870157894d3cb..b3be4cfd30603afb99c8688677959f16199f7b54 100644
--- a/lib/manager/bundler/artifacts.spec.ts
+++ b/lib/manager/bundler/artifacts.spec.ts
@@ -12,6 +12,7 @@ import * as _bundlerHostRules from './host-rules';
 import { BinarySource } from '../../util/exec/common';
 import { setUtilConfig } from '../../util';
 import { resetPrefetchedImages } from '../../util/exec/docker';
+import { BUNDLER_UNKNOWN_ERROR } from '../../constants/error-messages';
 
 const fs: jest.Mocked<typeof _fs> = _fs as any;
 const exec: jest.Mock<typeof _exec> = _exec as any;
@@ -49,6 +50,7 @@ describe('bundler.updateArtifacts()', () => {
     env.getChildProcessEnv.mockReturnValue(envMock.basic);
     bundlerHostRules.findAllAuthenticatable.mockReturnValue([]);
     resetPrefetchedImages();
+    global.repoCache = {};
     await setUtilConfig(config);
   });
   it('returns null by default', async () => {
@@ -260,4 +262,48 @@ describe('bundler.updateArtifacts()', () => {
       expect(execSnapshots).toMatchSnapshot();
     });
   });
+  it('throws BUNDLER_UNKNOWN_ERROR when failing in lockFileMaintenance true', async () => {
+    const execError = new Error();
+    (execError as any).stdout = ' foo was resolved to';
+    (execError as any).stderr = '';
+    platform.getFile.mockResolvedValueOnce('Current Gemfile.lock');
+    fs.outputFile.mockResolvedValueOnce(null as never);
+    const execSnapshots = mockExecAll(exec, execError);
+    platform.getRepoStatus.mockResolvedValueOnce({
+      modified: ['Gemfile.lock'],
+    } as Git.StatusResult);
+    await expect(
+      updateArtifacts({
+        packageFileName: 'Gemfile',
+        updatedDeps: [],
+        newPackageFileContent: '{}',
+        config: {
+          ...config,
+          isLockFileMaintenance: true,
+        },
+      })
+    ).rejects.toThrowError(BUNDLER_UNKNOWN_ERROR);
+    expect(execSnapshots).toMatchSnapshot();
+  });
+  it('performs lockFileMaintenance', async () => {
+    platform.getFile.mockResolvedValueOnce('Current Gemfile.lock');
+    fs.outputFile.mockResolvedValueOnce(null as never);
+    const execSnapshots = mockExecAll(exec);
+    platform.getRepoStatus.mockResolvedValueOnce({
+      modified: ['Gemfile.lock'],
+    } as Git.StatusResult);
+    fs.readFile.mockResolvedValueOnce('Updated Gemfile.lock' as any);
+    expect(
+      await updateArtifacts({
+        packageFileName: 'Gemfile',
+        updatedDeps: [],
+        newPackageFileContent: '{}',
+        config: {
+          ...config,
+          isLockFileMaintenance: true,
+        },
+      })
+    ).not.toBeNull();
+    expect(execSnapshots).toMatchSnapshot();
+  });
 });
diff --git a/lib/manager/bundler/artifacts.ts b/lib/manager/bundler/artifacts.ts
index 3e271f52da1fb8b83e8892b81dbc5eaeda1eea25..b2b7c07a52dbc699bee9330433059b167bceafa4 100644
--- a/lib/manager/bundler/artifacts.ts
+++ b/lib/manager/bundler/artifacts.ts
@@ -2,6 +2,7 @@ import {
   getSiblingFileName,
   readLocalFile,
   writeLocalFile,
+  deleteLocalFile,
 } from '../../util/fs';
 import { exec, ExecOptions } from '../../util/exec';
 import { logger } from '../../logger';
@@ -85,10 +86,21 @@ export async function updateArtifacts(
     logger.debug('No Gemfile.lock found');
     return null;
   }
+
+  if (config.isLockFileMaintenance) {
+    await deleteLocalFile(lockFileName);
+  }
+
   try {
     await writeLocalFile(packageFileName, newPackageFileContent);
 
-    const cmd = `bundle lock --update ${updatedDeps.join(' ')}`;
+    let cmd;
+
+    if (config.isLockFileMaintenance) {
+      cmd = 'bundle lock';
+    } else {
+      cmd = `bundle lock --update ${updatedDeps.join(' ')}`;
+    }
 
     let bundlerVersion = '';
     const { bundler } = compatibility;
@@ -171,7 +183,7 @@ export async function updateArtifacts(
       throw new Error(BUNDLER_INVALID_CREDENTIALS);
     }
     const resolveMatchRe = new RegExp('\\s+(.*) was resolved to', 'g');
-    if (output.match(resolveMatchRe)) {
+    if (output.match(resolveMatchRe) && !config.isLockFileMaintenance) {
       logger.debug({ err }, 'Bundler has a resolve error');
       const resolveMatches = [];
       let resolveMatch;
diff --git a/lib/manager/bundler/index.ts b/lib/manager/bundler/index.ts
index d94aecf09e6806c94b215ef2de30ac6043f43659..bcb44731b29ad993664e80bd7326cb557412f9d3 100644
--- a/lib/manager/bundler/index.ts
+++ b/lib/manager/bundler/index.ts
@@ -6,6 +6,7 @@ import { LANGUAGE_RUBY } from '../../constants/languages';
 import * as rubyVersioning from '../../versioning/ruby';
 
 const language = LANGUAGE_RUBY;
+export const supportsLockFileMaintenance = true;
 
 /*
  * Each of the below functions contain some explanations within their own files.
diff --git a/lib/util/fs.ts b/lib/util/fs.ts
index 3532ac9be75996bcf6a0f6a0402d4624baabe924..d71d088496acc635b6b74c0ea5c9811f6b911570 100644
--- a/lib/util/fs.ts
+++ b/lib/util/fs.ts
@@ -1,5 +1,5 @@
 import { parse, join } from 'upath';
-import { outputFile, readFile } from 'fs-extra';
+import { outputFile, readFile, remove } from 'fs-extra';
 import { logger } from '../logger';
 import { RenovateConfig } from '../config/common';
 
@@ -47,3 +47,7 @@ export async function writeLocalFile(
   const localFileName = join(localDir, fileName);
   await outputFile(localFileName, fileContent);
 }
+
+export async function deleteLocalFile(fileName: string): Promise<void> {
+  await remove(fileName);
+}