Installing Drupal with Composer
Although there are different ways to get Drupal onto a server, the most reliable method for modern projects is to use Composer. Composer doesn't just download Drupal's core files — it also manages the PHP packages, modules, and other dependencies Drupal needs.
This means that when we move the project to a different server, instead of copying every dependency one by one, we can recreate them all from the composer.json and composer.lock files.
Drupal's official documentation also recommends Composer and the drupal/recommended-project structure for new projects.
Before You Start the Installation
First, we can check that PHP and Composer are installed on the server.
To see the PHP version:
php -vFor Composer:
composer --versionwe can use this.
For example, if we get an output like:
Composer version 2.8.12this means Composer is available.
Drupal 11 requires Composer 2.7.0 or newer.
Creating the Drupal Project
To create a new Drupal project:
composer create-project drupal/recommended-project my-drupal-sitewe can use this command.
Here,
drupal/recommended-projectis Drupal's recommended project template.
my-drupal-siteis the project directory that will be created.
Once the command finishes, a structure similar to the following is created:
my-drupal-site/
├── composer.json
├── composer.lock
├── vendor/
└── web/Drupal's executable web files live inside the web directory. Files that visitors don't need direct access to, such as vendor, stay outside the web root. This is one of the main reasons Drupal recommends the recommended-project template.
Why Is the Web Root the web Directory?
In classic PHP projects, the web server's document root can be the project directory itself.
In Drupal's recommended Composer structure, however, the web server's document root should be:
/var/www/my-drupal-site/webIn other words, we point the Nginx or Apache configuration to:
/var/www/my-drupal-site/webinstead of:
/var/www/my-drupal-siteThis way, files such as:
composer.json
composer.lock
vendor/aren't directly accessible through the web.
Installing a Specific Drupal Version
We might not always want to install the latest version.
For example, if the project needs to start on a specific Drupal version, we can specify the version in the Composer command:
composer create-project drupal/recommended-project:11.2.0 my-drupal-siteIn this case, Composer creates the project based on the specified Drupal version.
If we want to start with the current stable version without pinning a specific version:
composer create-project drupal/recommended-project my-drupal-siteis enough. Drupal's official installation documentation also uses this command as the basic installation method.
What Is the composer.json File For?
After installation, there is a composer.json file at the project root.
This file defines which packages the project needs.
For example:
{
"require": {
"drupal/core-recommended": "^11",
"drupal/core-composer-scaffold": "^11"
}
}In this example, the project tells Composer it needs the Drupal 11 core packages.
Later, when we add a new Drupal module to the project, Composer updates this file automatically.
Why Does composer.lock Matter?
While composer.json specifies which packages can be used, composer.lock keeps the exact package versions used at installation.
For example, when two developers set up the same project on their own machines, running:
composer installComposer installs the versions recorded in composer.lock as closely as possible.
This is why the composer.lock file needs to be added to Git in Drupal projects.
Its purpose is to ensure the same dependency versions are used across different development, testing, and production environments.
The Difference Between composer install and composer update
These are two of the most commonly confused commands when using Composer.
composer installinstalls the versions recorded in the existing composer.lock file.
For example, when we bring the project to a new server via Git, we typically proceed like this:
git clone [email protected]:project/drupal.git
cd drupal
composer installcomposer update, on the other hand, can look for newer allowed versions of dependencies and can change the composer.lock file.
This is why it isn't right to run:
composer updateout of habit on a production server.
An update should first be done in the development environment, the resulting composer.lock change should be tested, and only then transferred to production through deployment.
How Do You Add a Drupal Module?
Adding a contributed module to a Composer-managed Drupal project is quite simple.
For example, to add the Pathauto module:
composer require drupal/pathautowe can use this.
Composer downloads the required package and updates the project's composer.json and composer.lock files. Drupal's official Composer documentation also recommends adding contrib modules this way.
However, this doesn't automatically enable the module inside Drupal.
If we're using Drush, we can also run:
vendor/bin/drush en pathautoSo:
composer requireadds the package to the project,
drush enenables the module inside Drupal.
Development Packages
Some packages are only needed in the development environment.
For example, the Devel module can be added like this:
composer require --dev drupal/develHere,
--devindicates that the package isn't required for production.
When:
composer install --no-devis used in production, development dependencies aren't installed.
Drupal's official documentation also recommends using --no-dev in production installations.
Installing an Existing Project on a New Server
Let's say we're bringing a Drupal project from Git to a new server.
For example:
git clone [email protected]:project/site.git
cd siteIt's normal if the project's vendor directory isn't in Git.
To recreate the required dependencies:
composer installwe run this.
In production:
composer install --no-devcan be used.
Composer reads the composer.lock file during this process and redownloads the packages the project needs.
That's why keeping the vendor directory in the Git repository usually isn't necessary in most Composer-based projects.
Why Does the vendor Directory Matter?
Having only the web/core directory isn't enough for Drupal to run.
Symfony components and other PHP dependencies live inside vendor.
For example, if Composer isn't run after pulling Drupal's code via Git, an error like:
vendor/autoload.php not foundcan appear.
In that case, the usual fix is running:
composer installDrupal's official documentation specifically states that dependencies need to be installed with Composer in codebases obtained via Git.
Updating Drupal Core with Composer
One of Composer's key advantages is that updates can be done in a controlled way.
When we only want to update Drupal core packages, for example:
composer update drupal/core-recommended \
drupal/core-composer-scaffold \
drupal/core-project-message \
--with-all-dependenciescan be used.
After updating, Drupal's database updates also need to be run.
If we're using Drush:
vendor/bin/drush updatedband then to clear the cache:
vendor/bin/drush crwe can run this.
Composer updates the package files; updatedb runs the database updates Drupal's modules need. These are two different operations.
Common Composer Errors
A significant portion of the errors encountered during installation come from the PHP version or missing PHP extensions.
For example, if we see an error like:
requires php >=8.xthe PHP version on the server might not meet the requirement of the package we're trying to install.
Similarly, errors like:
ext-gd is missingor:
ext-xml is missingindicate that the relevant PHP extension isn't present on the system.
So checking only:
php -visn't always enough.
To see the active PHP extensions:
php -mwe can use this.
Another important point is that the PHP used by the web server can differ from the PHP version in the terminal. Especially on servers with multiple PHP versions installed, Composer CLI uses whichever PHP version is on its own path.
Should We Run Composer as Root?
Running:
sudo composer installon the server might seem to solve permission issues at first glance, but it can cause the project files to end up owned by root.
Afterwards, when the web server or the deployment user tries to access these files, new permission problems can arise.
That's why it's healthier to run Composer commands with the regular user who owns the project whenever possible.
For example, before running:
cd /var/www/my-drupal-site
composer installlike this, you should make sure the directory's owner and permission structure are correct.
A Quick Installation Summary
If we're starting a simple Drupal project from scratch, the basic process can be as short as this:
composer create-project drupal/recommended-project my-drupal-site
cd my-drupal-site
composer require drush/drushAfter that, we can set the web server's document root to:
my-drupal-site/weband move on to the Drupal installation screen with the database information we prepared earlier.
Conclusion
Composer isn't just a package-downloading tool in modern Drupal projects. It lets us centrally manage which versions of core, contributed modules, and PHP dependencies are used.
Starting a new Drupal project with:
composer create-project drupal/recommended-project my-drupal-sitelets us use Drupal's recommended project structure.
Going forward, managing module additions, core updates, and deployment through Composer makes it easier for development, testing, and production environments to run on the same dependencies as much as possible.