diff --git a/lib/manager/composer/artifacts.js b/lib/manager/composer/artifacts.js
index 583e710a6ab892048a12bfbbc2d242da524d06b6..0c9642afbded6d87cfff9f716d684841e49172d9 100644
--- a/lib/manager/composer/artifacts.js
+++ b/lib/manager/composer/artifacts.js
@@ -35,6 +35,9 @@ async function updateArtifacts(
     const localPackageFileName = upath.join(config.localDir, packageFileName);
     await fs.outputFile(localPackageFileName, newPackageFileContent);
     const localLockFileName = upath.join(config.localDir, lockFileName);
+    if (config.isLockFileMaintenance) {
+      await fs.remove(localLockFileName);
+    }
     const authJson = {};
     let credentials = hostRules.find({
       hostType: 'github',
@@ -115,10 +118,16 @@ async function updateArtifacts(
       logger.info('Running composer via global composer');
       cmd = 'composer';
     }
-    const args =
-      ('update ' + updatedDeps.join(' ')).trim() +
-      ' --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader --with-dependencies';
-    logger.debug({ cmd, args }, 'composer update command');
+    let args;
+    if (config.isLockFileMaintenance) {
+      args = 'install';
+    } else {
+      args =
+        ('update ' + updatedDeps.join(' ')).trim() + ' --with-dependencies';
+    }
+    args +=
+      ' --ignore-platform-reqs --no-ansi --no-interaction --no-scripts --no-autoloader';
+    logger.debug({ cmd, args }, 'composer command');
     ({ stdout, stderr } = await exec(`${cmd} ${args}`, {
       cwd,
       shell: true,
diff --git a/lib/manager/composer/index.js b/lib/manager/composer/index.js
index 568ada54abc83a3b76b2d7b5b566778f7d83ef54..e31490e30d7ca86d5572c08e37e29bb89e4b3f5f 100644
--- a/lib/manager/composer/index.js
+++ b/lib/manager/composer/index.js
@@ -11,6 +11,5 @@ module.exports = {
   language,
   updateDependency,
   getRangeStrategy,
-  // TODO: support this
-  // supportsLockFileMaintenance: true,
+  supportsLockFileMaintenance: true,
 };
diff --git a/lib/workers/branch/get-updated.js b/lib/workers/branch/get-updated.js
index f8b0c54253566d29e1fb9ef5c915c22ea8694d5d..fe955912295b9c44935089ccf69214fb0c6a86bf 100644
--- a/lib/workers/branch/get-updated.js
+++ b/lib/workers/branch/get-updated.js
@@ -11,14 +11,16 @@ async function getUpdatedPackageFiles(config) {
   const updatedFileContents = {};
   const packageFileManagers = {};
   const packageFileUpdatedDeps = {};
-
+  const lockFileMaintenanceFiles = [];
   for (const upgrade of config.upgrades) {
     const { manager, packageFile, depName } = upgrade;
     packageFileManagers[packageFile] = manager;
     packageFileUpdatedDeps[packageFile] =
       packageFileUpdatedDeps[packageFile] || [];
     packageFileUpdatedDeps[packageFile].push(depName);
-    if (upgrade.updateType !== 'lockFileMaintenance') {
+    if (upgrade.updateType === 'lockFileMaintenance') {
+      lockFileMaintenanceFiles.push(packageFile);
+    } else {
       const existingContent =
         updatedFileContents[packageFile] ||
         (await platform.getFile(packageFile, config.parentBranch));
@@ -90,6 +92,34 @@ async function getUpdatedPackageFiles(config) {
       }
     }
   }
+  if (!config.parentBranch) {
+    // Only perform lock file maintenance if it's a fresh commit
+    for (const packageFile of lockFileMaintenanceFiles) {
+      const manager = packageFileManagers[packageFile];
+      const updateArtifacts = get(manager, 'updateArtifacts');
+      if (updateArtifacts) {
+        const packageFileContents =
+          updatedFileContents[packageFile] ||
+          (await platform.getFile(packageFile, config.parentBranch));
+        const results = await updateArtifacts(
+          packageFile,
+          [],
+          packageFileContents,
+          config
+        );
+        if (is.nonEmptyArray(results)) {
+          for (const res of results) {
+            const { file, artifactError } = res;
+            if (file) {
+              updatedArtifacts.push(file);
+            } else if (artifactError) {
+              artifactErrors.push(artifactError);
+            }
+          }
+        }
+      }
+    }
+  }
   return {
     parentBranch: config.parentBranch, // Need to overwrite original config
     updatedPackageFiles,
diff --git a/lib/workers/pr/pr-body.js b/lib/workers/pr/pr-body.js
index 179b0e904f5d13d0ae23c035fc9ab0355e3ff270..fdd3b7211c3c694c515af92329fe0d72a23657e3 100644
--- a/lib/workers/pr/pr-body.js
+++ b/lib/workers/pr/pr-body.js
@@ -159,7 +159,7 @@ async function getPrBody(config) {
 
   if (config.updateType === 'lockFileMaintenance') {
     prBody +=
-      ':wrench: This Pull Request updates `package.json` lock files to use the latest dependency versions.\n\n';
+      ':wrench: This Pull Request updates lock files to use the latest dependency versions.\n\n';
   }
 
   if (config.isPin) {
diff --git a/test/manager/composer/artifacts.spec.js b/test/manager/composer/artifacts.spec.js
index 5bf5e76a76af5d522a779c4f8fa9adda0cb2110a..5fbc84598fe0a6b692ddc9af2711983816fab512 100644
--- a/test/manager/composer/artifacts.spec.js
+++ b/test/manager/composer/artifacts.spec.js
@@ -73,6 +73,21 @@ describe('.updateArtifacts()', () => {
       await composer.updateArtifacts('composer.json', [], '{}', config)
     ).not.toBeNull();
   });
+  it('performs lockFileMaintenance', async () => {
+    platform.getFile.mockReturnValueOnce('Current composer.lock');
+    exec.mockReturnValueOnce({
+      stdout: '',
+      stderror: '',
+    });
+    fs.readFile = jest.fn(() => 'New composer.lock');
+    platform.getRepoStatus.mockResolvedValue({ modified: ['composer.lock'] });
+    expect(
+      await composer.updateArtifacts('composer.json', [], '{}', {
+        ...config,
+        isLockFileMaintenance: true,
+      })
+    ).not.toBeNull();
+  });
   it('supports docker mode', async () => {
     platform.getFile.mockReturnValueOnce('Current composer.lock');
     exec.mockReturnValueOnce({
diff --git a/test/workers/branch/__snapshots__/get-updated.spec.js.snap b/test/workers/branch/__snapshots__/get-updated.spec.js.snap
index ef9ad00feaf863f3f00c619bad12a38abd766d2f..0b5271543dc9f668eef5634cb50ddeb30f6fe829 100644
--- a/test/workers/branch/__snapshots__/get-updated.spec.js.snap
+++ b/test/workers/branch/__snapshots__/get-updated.spec.js.snap
@@ -60,3 +60,31 @@ Object {
   ],
 }
 `;
+
+exports[`workers/branch/get-updated getUpdatedPackageFiles() handles lockFileMaintenance 1`] = `
+Object {
+  "artifactErrors": Array [],
+  "parentBranch": undefined,
+  "updatedArtifacts": Array [
+    Object {
+      "contents": "some contents",
+      "name": "composer.json",
+    },
+  ],
+  "updatedPackageFiles": Array [],
+}
+`;
+
+exports[`workers/branch/get-updated getUpdatedPackageFiles() handles lockFileMaintenance error 1`] = `
+Object {
+  "artifactErrors": Array [
+    Object {
+      "name": "composer.lock",
+      "stderr": "some error",
+    },
+  ],
+  "parentBranch": undefined,
+  "updatedArtifacts": Array [],
+  "updatedPackageFiles": Array [],
+}
+`;
diff --git a/test/workers/branch/get-updated.spec.js b/test/workers/branch/get-updated.spec.js
index 4bacff0a4a51e33fcc3c198a64d922c58fbe9e1b..a00e4e988052fb730f12e0d5d3c89ff45b44b293 100644
--- a/test/workers/branch/get-updated.spec.js
+++ b/test/workers/branch/get-updated.spec.js
@@ -55,6 +55,40 @@ describe('workers/branch/get-updated', () => {
       const res = await getUpdatedPackageFiles(config);
       expect(res).toMatchSnapshot();
     });
+    it('handles lockFileMaintenance', async () => {
+      // config.parentBranch = 'some-branch';
+      config.upgrades.push({
+        manager: 'composer',
+        updateType: 'lockFileMaintenance',
+      });
+      composer.updateArtifacts.mockReturnValue([
+        {
+          file: {
+            name: 'composer.json',
+            contents: 'some contents',
+          },
+        },
+      ]);
+      const res = await getUpdatedPackageFiles(config);
+      expect(res).toMatchSnapshot();
+    });
+    it('handles lockFileMaintenance error', async () => {
+      // config.parentBranch = 'some-branch';
+      config.upgrades.push({
+        manager: 'composer',
+        updateType: 'lockFileMaintenance',
+      });
+      composer.updateArtifacts.mockReturnValue([
+        {
+          artifactError: {
+            name: 'composer.lock',
+            stderr: 'some error',
+          },
+        },
+      ]);
+      const res = await getUpdatedPackageFiles(config);
+      expect(res).toMatchSnapshot();
+    });
     it('handles lock file errors', async () => {
       config.parentBranch = 'some-branch';
       config.upgrades.push({
diff --git a/test/workers/pr/__snapshots__/index.spec.js.snap b/test/workers/pr/__snapshots__/index.spec.js.snap
index 05de881dd5b5e6c1c4d344e0adc5bc5dfee4fd10..8885b22f7b82f6f5f044341ee8df9f44731beb64 100644
--- a/test/workers/pr/__snapshots__/index.spec.js.snap
+++ b/test/workers/pr/__snapshots__/index.spec.js.snap
@@ -140,7 +140,7 @@ note 2
 
 :abcd: If you wish to disable git hash updates, add \`\\":disableDigestUpdates\\"\` to the extends array in your config.
 
-:wrench: This Pull Request updates \`package.json\` lock files to use the latest dependency versions.
+:wrench: This Pull Request updates lock files to use the latest dependency versions.
 
 ---