---
title: "Migrating a Drupal website into a Composer-managed build"
date: "2024-02-14T06:18:38+00:00"
summary: "Streamline your Drupal website migration to a Composer-managed build with our step-by-step guide. Learn to create a robust composer.json file, restructure your repository, and efficiently manage dependencies for a smoother development workflow."
image:
type: "page"
url: "/acquia-cloud-platform/migrating-drupal-website-composer-managed-build"
id: "dbf5a75f-0567-45c0-ba02-6bf90a89b435"
---

Migrating an existing website running Drupal into a Composer-managed build requires you build a `composer.json` file including all the required packages for your website.

The structure of your repository should shift as follows:

![cloud-platform_repository-structure.png](https://acquia.widen.net/content/429ab854-5154-421e-af11-0b37d905d8ed/web/cloud-platform_repository-structure.png?w=480&itok=TuhYi07m)

The key differences include the new top-level `composer.json` and `composer.lock` files, and the top-level location of the vendor directory.

Before you start
----------------

To compare Composer changes, Acquia recommends you start with a website generally structured the way you want the website built.

Note

In a Drupal application that uses the Drupal recommended project, the default web root is `docroot/`. This is the standard for running a Drupal application on [Cloud Platform](/acquia-cloud-platform). For more information, see `https://github.com/acquia/drupal-recommended-project/blob/master/composer.json#L75`.

*   _Contributed modules_: Drupal best practice is to place contributed modules in `docroot/modules/contrib`. If your modules are not in `docroot/modules/contrib`, consider moving all contributed modules to the specified location.
*   _Custom modules_: Drupal best practice is to place custom modules in `docroot/modules/custom`. If your modules are not in `docroot/modules/custom`, consider moving all custom modules to the specified location.
*   _Themes_: Drupal best practice is to reflect the same `custom/contrib` structure for themes as for modules. Each one of the changes may require you to run `drush cr` to ensure your Drupal website knows the new `module/theme` location. Acquia recommends you run `drush cr` first to reduce potential complications migrating to Composer.
*   _Custom code_: If you have made any modifications to Drupal core files or contributed module files, you must create patches for the files, and store them in a top level `patches/` directory. The `composer update` command will overwrite all core and contributed module files, but the command applies patches after an update is applied. Creating the patches now will save you trouble later.
    
    If you have modified the `composer.json` file located at `docroot/composer.json`, you must make a note of the modifications as the `composer update` command overwrites the `composer.json` file. After the `composer update` command completes execution, add your requirements to the top-level `composer.json` file.
    

All the changes should be committed, tagged, and, if possible, merged to master: your new, permanent, file structure.

Procedure
---------

1.  Create a development branch containing your `composer.json` file. Do not create a development branch on `master`. The branch should contain all code required to work on production.
2.  Secure a working template for you to use as reference. Acquia recommends that you use the following template:
    
    `https://github.com/acquia/acquia-ra-composer/blob/master/composer.json`
    
3.  Save the file at the top level of your repository: `repo/composer.json`.
    
    Note
    
    The instructions in the procedure are based on the linked `composer.json` file.
    
4.  To simplify the migration, Acquia strongly recommends you install the exact version of Drupal and all contributed modules already running on your website.
    
    To pin the module to a specific version, open your `composer.json` file, and then edit the `require` section based on the following:
    
        "require": {
            "drupal/core-composer-scaffold": "^9.2",
            "composer/installers": "^1.0"
        },
    
    *   _Drupal core_: Require Drupal core by changing `"drupal/core-recommended": "^<major-version>.<minor-version>"`, to your [current Drupal version](/service-offerings/guide/software-life-cycle#supported-drupal-version), for example, `"drupal/core-recommended": "<major-version>.<minor-version>.<patch>"`. Specifying the entire version is called _pinning_ a package. Pinning has the advantage of ensuring a specific version is installed, but pinning also prevents using the `composer update drupal/core-recommended --with-dependencies` command for updates. _Unpin_ a module after Composer migration testing is complete.
        
    *   _Contributed modules_: All modules required to run your website on production should be listed in the `require` section. Start with contributed modules.
        
        Generate a list of all enabled contributed modules, which you can do with the following Drush command:
        
    
        drush pml --status=Enabled --no-core
    
    If you use multisites, inspect all multisites to ensure your contributed module list is complete. Results should display similarly to the following:
    
    Package
    
    Name
    
    Type
    
    Administration
    
    Admin Toolbar (admin\_toolbar)
    
    Module
    
    Chaos tool suite
    
    Chaos tools (ctools)
    
    Module
    
    Other
    
    Pathauto (pathauto)
    
    Module
    
    Other
    
    Token (token)
    
    Module
    
    Other
    
    MTM Foundation (`mtm_foundation`)
    
    Theme
    
    Other
    
    ZURB Foundation (`zurb_foundation`)
    
    Theme
    
    Add each module and theme to the `require` section. Using the previous example, your `require` section should display like the following:
    
        "require": {
            "drupal/core-composer-scaffold": "^9.2",
            "drupal/core-recommended": "^9.2",
            "drupal/admin_toolbar": "^1.1",
            "drupal/ctools": "^3.0",
            "drupal/pathauto": "^1.0",
            "drupal/token": "^1.0",
            "drupal/zurb_foundation": "^6.x",
            "composer/installers": "^1.0"
        },
    
    To generate a list of installed modules preformatted for Composer:
    
    1.  Install and enable the [composerize](https://www.drupal.org/project/composerize) module in a development version of your site.
    2.  Go to `admin/reports/composerize/` on your site.
    
    Copy the list of dependencies from the `require` section of `composer.json` on the page. Paste the list of modules into the `require` section of your `composer.json` template.
    
    *   _Module paths_: Requiring `composer/installers` allows you to specify, in the extras section, where composer should download packages of a particular type:
    
        "extra": {
            "installer-paths": {
                "docroot/core": ["type:drupal-core"],
                "docroot/modules/contrib/{$name}": ["type:drupal-module"],
                "docroot/profiles/contrib/{$name}": ["type:drupal-profile"],
                "docroot/themes/contrib/{$name}": ["type:drupal-theme"],
                "docroot/libraries/{$name}": ["type:drupal-library"]
            }
        }
    
    Drupal best practice recommends placing contributed modules in `docroot/modules/contrib`. Your current branch should already reflect the decision you made before starting the migration process. If your modules are not in `docroot/modules/contrib`, you must change the installer path by removing `/contrib`.
    
    *   _Libraries_: If contributed modules require manually adding libraries (for example, the module does not use a `composer.json` file to download its required libraries), you may add the libraries directly to your `require` section. For an example, see `enyo/dropzone` in both the `require` section, and in the installer-paths section of the following template:
        
        `https://github.com/acquia/acquia-ra-composer/blob/master/composer-templates/composer-libraries.json`
        
    *   _Custom modules and themes_: The following methods handle custom modules and themes:
        *   Circumvent Composer entirely, and directly commit your custom modules and themes to your repository.
        *   Create your custom modules, and themes as Composer packages, ensure Composer downloads them, and then include the custom modules, and themes as you would other packages.
    
    Circumventing Composer is easier than creating Composer packages, but if more than one Composer-built website uses your theme or module, creating your custom code as discrete Composer packages may be more efficient and developer-friendly.
    
    In either case, custom code should live in demarcated custom directories:
    
        docroot/modules/custom
        docroot/themes/custom
    
    Custom directories allow you to delete the contributed themes and modules by deleting the parent contributed directory entirely, and allowing Composer to rebuild the contributed directory from scratch.
    
5.  Delete the following directories (including both the directories’ contents and the directories themselves):
    
        docroot/core
        docroot/modules/contrib
        docroot/themes/contrib
        docroot/vendor
    
6.  Run the following command:
    
        composer install
    
    The `composer install` command should install all the packages required in the `composer.json` file into the proper directory, create a `composer.lock` file, and recreate `docroot/autoload.php` if the file does not exist.
    

Important

The top-level composer.json in turn must define where the web root is located. It does so via the locations mapping, as shown below:

      "extra": {
        "drupal-scaffold": {
            "locations": {
                "web-root": "./docroot"
            },
         ...
        }
    }

Note

If you have an existing `docroot/autoload.php`, you can delete or manually modify the following path in `docroot/autoload.php` so that it looks like the following:

    return require __DIR__ . '/../vendor/autoload.php';

1.  Test your now-working website by using `git diff` to compare directories or particular files. Pinned modules or core files (except `autoload.php`) will display little differences. The entire vendor directory has moved, and the packages in the vendor directory are different as there may be a more recent version than the version on your existing website. The changes are fine as the module itself is the same. Ensure you check all parts of your Drupal website, noting if you must run `drush cr`, or if modules are missing.
2.  Continue to delete, change the `composer.json` file, and install until your website is fully working.
3.  Whenever your website is ready, you should commit the `composer.json` changes, `composer.lock`, and all generated code. Remember you are committing pinned versions of your modules ensuring composer installs the exact versions running on your existing website. Your next step, either in the existing branch, or after the existing branch has been tested and merged to master, is to change all the versioning in the `require` section to use the caret (`^`), and more open versioning:
    
        "require": {
            "drupal/core-composer-scaffold": "^9.2",
            "drupal/core-recommended": "^9.2",
            "drupal/admin_toolbar": "^1.1",
            "drupal/ctools": "^3.0",
            "drupal/pathauto": "^1.0",
            "drupal/token": "^1.0",
            "drupal/zurb_foundation": "^6.x",
            "composer/installers": "^1.0"
        },
    

If you run `composer update drupal/core-recommended --with-dependencies`, Composer updates your website to the latest version. Ensure that you pin your website to a version after you test the migration with the pinned version.