Converting a Drupal 8 or 9 site to use Composer

February 14, 2022

Composer is the preferred way to manage modules and dependencies in Drupal 8 and above. If your site is not using composer, this guide will walk you through every step of converting an older Drupal site to a composer-based Drupal site.

Step 1: Generate a composer.json that matches your current modules

This tutorial assumes you already have composer installed. If not, see the Composer Installation documentation.

1. If you have an existing composer.json file, make a backup of it for reference:

cp composer.json composer.json.bak

2. Install composerize-drupal. This utility will create a composer.json file that matches your currently installed core and modules:

composer global require grasmash/composerize-drupal

If you get composer errors such as “Conclusion: don’t install grasmash/composerize-drupal 1.2.1”, try the following steps:

  • Navigate to your global composer folder (usually found in /Users/[user]/.composer) and check for composer.json and composer.lock. If those exist, make backups of each (similar to step 1) and then remove composer.lock and remove all content from the composer.json file so that it just contains an open and closing brace: {}
  • Run this command: composer update
  • Try installing composerize-drupal again

3. Generate a new composer.json. This example uses the –exact-versions command so that composer.json ends up with the exact versions of the modules you have installed. It also assumes that drupal does not live in a subdirectory like docroot/ or web/ . See the documentation for more options.

composer composerize-drupal --composer-root=. --drupal-root=. --exact-versions --no-update

4. Look for messages from the output of this command that say: ”Could not determine correct version for project”. If you find some, jump to “Determining module version number if they are missing from the .info.yml file” below.

5. Run composer update:

composer update

If you ran into missing module numbers in step 4, you may get “discard changes?” messages for the modules without version numbers. It is probably safe to say “yes” to these.

6. Check to see what changed in your modules directory. The goal for right now is to make it so there are no differences here aside from composer.json and composer.lock files that the composerizer scrpt removed.; the command should return no output. That way you’ll known you have a composer file that perfectly matches your site. (This command assumes your project is stored in a git repository):

git status modules/

It is safe to see changes to composer.json and composer.lock files here. If you see other changes, the first thing to do is make sure they’re not just file mode changes from your local environment. Try:

git config core.fileMode false
git status modules/

If you still see changes, either: a) you have dev versions of a module installed, and the dev version has been updated or b) patches need to be applied (see next step)

Determining module version number if they are missing from the .info.yml file

(This section is irrelevant if you did not receive “Could not determine correct version…” messages from composerize-drupal)

When trying to create a matching composer.json file, if the version number is missing from the .info.yml file, you’ll have to identify the version manually. This usually happens for modules that were installed from git or for which there’s no official release.

First, check composer.json.bak

Sometimes the original composer.json file had entries for modules because some people were using composer and some weren’t. Check to see if there’s an entry in this file for the module in question. If so, use the same version.

Try the dev version of the module

I got “Could not determine correct version for project drupal/entityqueue.”. I noticed there was a .git directory in the modules/contrib/entityqueue directory, so I went into that directory and did git status:

cd modules/contrib/entityqueue
git status

git status told me that I’m on branch 8.x-1.1, but someone installed from git, which probably means they wanted the dev branch. I edited composer.json to reflect 1.x-dev:

"drupal/entityqueue": "1.x-dev"

Note that the absence of a .git directory doesn’t mean it’s not a dev version – it just means someone installed the dev version manually. Most likely, if you have version-less modules, the dev version was installed. It’s probably safe to continue using the dev version, but you’ll need to test the functionality provided by the module.

Step 2: Patches

If modules or core were patched, you’ll need to make sure those patches get reapplied (note that if you’re about to do a core/module upgrade, this step may not be necessary because the patches may have already made it into the newest module release. In this case, see “Determining whether you still need a patch” below.

1. Check your composer.json.bak file for a ‘patches’ section. Mine looked like what’s below. If you don’t have a ‘patches’ section in composer.json.bak, you either don’t have any module patches to worry about, or people were patching manually.

My composer.json.bak patches section looked like this:

"patches": {
            "drupal/core": {
                "Fix URI exception if telephone number < 5 digits": ""
            "drupal/menu_block": {
                        "Menu parent as block title": "",
                "Add option to make the starting level follow the active menu item": ""
            "drupal/draggableviews": {
                "Fix duplicates": ""
            "drupal/webform": {
                "Fix machine name starting with an underscore throwing errors": ""

2. If you have a ‘patches’ section, try simply copying it into the composer.json file. It goes under “extras”, and there should already be an empty “patches” section in there.

3. Try running composer update after copying the patches section:

composer update

4. You may get a message that some patches failed. In this case you’ll have to first confirm whether the patch is still necessary (try googling for the patch and see if it’s been incorporated into the latest module), or if a later patch has been created.

5. If composer update runs cleanly, then you are now able to use composer to manage modules. Moving forward, only install or update modules using composer.

Step 3: Managing Drupal Core with Composer

Most likely, you will want to manage Drupal core using composer. This makes upgrades far easier, although note that on Pantheon, it prevents use of their automatic updates feature. However, the benefits of having composer manage Drupal core outweigh this drawback.

Pantheon has their own documentation for this process at the following link. If you run into problems, it may be helpful to consult them: Convert a Standard Drupal 8 Site to a Composer Managed Site

1. First, let’s set up a convenient environment variable so that the commands below work exactly as shown. Run the following from your terminal, replacing “my-site” with the name of your site in pantheon (the same name you would use if you were running terminus).

export site=my-site

2. Checkout a new branch (replace ~/projects/my-site with the path to the site you want to composer-ify):

cd ~/projects/my-site
git checkout -b composerify

3. Create a completely new project directory that will house the ‘composerified’ version of your site.In this example we are using Pantheon’s Drupal 8 base install.

cd ../
composer create-project pantheon-systems/example-drops-8-composer $site-composer

So your parent directory (~/projects in this example) will look like this, with my-site being the original copy of the site, and my-site-composer being a brand new composerified version of Drupal 8:


4. Copy your pantheon upstream config into the composerified version:

cd $site-composer
cp ../$site/pantheon.upstream.yml .

5. This part takes some manual effort:

Open ../$site/composer.json from the steps above, and copy and paste all the drupal modules listed in the ‘require’ section into the ‘require’ section of ./composer.json . They all start with “drupal/” and look like this:

"drupal/draggableviews": "1.2.0",
"drupal/token": "1.5.0",
"drupal/module_filter": "3.1.0",
"drupal/svg_image": "1.8.0",

DO NOT copy over drupal/core, drupal/drupal, drupal/core/recommended, or anything related to Drupal core.

6. Open ../$site/composer.json (the original composer.json) file again, and copy the “patches” section into ./composer.json . Patches goes under “extra” in the composer.json section – there should already be a blank array here. If your existing composer.json has no patches listed, simply skip this step.

7. Make sure your “minimum stability” and “prefer-stable” settings in your new composer.json (in $site-composer/composer.json”) are the same as the original composer.json. For example, you may need to change “minimum stability” to “dev”:

  "minimum-stability": "dev",
  "prefer-stable": true,

8. Copy over custom themes, modules, and libraries (assumes you’re in the $site-composer directory):

cp -pvR ../$site/themes/custom/* ./web/themes/custom/
cp -pvR ../$site/modules/custom/* ./web/modules/custom/
mkdir ./web/modules/features
cp -pvR ../$site/modules/features/* ./web/modules/features/
mkdir ./web/libraries
cp -pvR ../$site/libraries/* ./web/libraries

9. Copy your settings.php files into the new project:

cp -pvR ../$site/sites/default/settings*.php ./web/sites/default/

10. Copy your config into the new project:

cp -pvR ../$site/sites/default/config/* ./config/

11. If hosting the site with Pantheon, prep for Pantheon deployment (skip this step otherwise);

composer prepare-for-pantheon

12. Run Composer install:

composer install 

13. Attach your new project to the git repository:

cp -r ../$site/.git ../$site-composer/
git add .
git commit -m "Convert to Composer based Drupal core"

14. Push the branch. The following command pushes to a branch called “composerify”:

git push origin composerify

Common Issues

Pantheon “No Site Detected”

1. Make sure web_docroot: true is set in pantheon.yml

2. If it is there and you still see this message, try making any change at all to the pantheon.yml file (add a comment or something), commit and push. It will force Pantheon to re-read it.