Skip to main content
Table of contents

Have you ever walked into a situation at work and just wondered how everything still runs smoothly? I mean, there are deployments with so many fail-safes stacked on a shaky foundation that it's almost a miracle they work at all. The real trick is gently nudging the team toward accepting change to develop a more stable and robust process—something that really lines up with Drupal best practices.

In many development environments I've seen, repositories are bogged down with too much data. For example, the vendor and contrib directories often end up in the repository, which adds hundreds of megabytes of unnecessary weight. This stuff really should be left out.

A lot of this tendency comes from past challenges. Take a recent experience with a team I joined: a standard deployment accidentally triggered an upgrade to a vendor package during the move to production. What seemed like a small tweak caused major headaches and took a lot of effort to sort out. It led to a real hesitation around adopting best practices, which was a mix of not following established procedures and not even knowing they existed.

So, this article is really here as a lasting resource. It's about guiding you on deploying your Drupal 10 site the right way. With Drupal 10, there are tons of new features and improvements that really step up web content management. To make the most of these enhancements, developers need a sharp deployment strategy that cuts down on errors and sticks to the best software development and maintenance practices. A big part of this strategy is using Composer effectively for managing dependencies and keeping the repository clean. Next time you're at the office, this could be a great piece to bring up, especially if you’re looking to steer some shop talk towards something productive.


1. Understanding the role of Composer in Drupal 10

Composer is a dependency management tool for PHP that allows you to declare the libraries your project depends on, and it manages (installs or updates) them for you. In Drupal 10, Composer is essential for ensuring that all components of your Drupal installation are at their correct versions and remain compatible with each other.

Key benefits of using Composer:

  • Dependency Management
    Composer handles package versions and ensures all dependencies are met, preventing compatibility issues
  • Autoloading
    Composer provides an autoloader capable of loading classes from include paths and from various class map formats
  • Scalability
    With Composer, adding, upgrading, or removing software is managed with minimal effort, which is crucial for maintaining large applications like Drupal.


2. Best practices for Drupal repositories

Setting up Drupal 10 project requires maintaining a clean repository by excluding certain directories and files. The objective is to implement and test a CI/CD pipeline that adheres to best practices by excluding the directories like vendor from source control.  This is actioned to keep the repository lightweight. Here's what should and should not be included in your version control system.

For ongoing projects where the Drupal 10 environment is already operational, the focus shifts towards maximising efficiency. This includes optimising the deployment process and ensuring that the CI/CD pipeline is streamlined to support rapid, reliable, and repeatable builds and deployments. Implementing these practices not only enhances performance but also improves the overall security and maintainability of the application.


What to exclude from the repository:

  • Vendor Directory: Contains third-party libraries managed by Composer. These are restorable via composer install
  • Contributed Modules and Themes: Should be managed via Composer and excluded from the repository
  • Core: The Drupal core itself should also be excluded from direct version control
  • Files Directory: Contains user-uploaded content and should not be in the repository to protect sensitive data
  • Private Directory: Holds sensitive information and should be kept out of the repository for security reasons
  • Temporary Directory: Transient files that do not need to be version controlled


1. Vendor directory

Path: /vendor/

Rationale: The vendor/ directory is created and managed by Composer and contains all the third-party PHP libraries and dependencies your project requires. These dependencies are defined in your composer.json file. Since Composer can recreate this directory by running composer install, there is no need to store it in your repository. Including this directory would unnecessarily increase the size of your repository and could lead to conflicts between dependencies when collaborating with others.


2. Contributed modules and themes

Paths: /modules/contrib/, /themes/contrib/, /profiles/contrib/

Rationale: Like the vendor/ directory, contributed modules and themes should be managed through Composer and defined in composer.json. This practice ensures that you maintain a record of the specific versions and dependencies your site needs without cluttering the repository with third-party code. This helps in tracking custom changes more clearly without the noise of external code changes each time an update is made.


3. Core

Path: /core/

Rationale: The Drupal core directory contains the primary Drupal installation files and directories. Managing this directory through Composer ensures that updates to Drupal core are handled consistently and cleanly through dependency management practices. Directly including core in the repository can lead to conflicts and challenges in tracking changes, especially during Drupal updates or when applying security patches.


4. Files directory

Path: /sites/*/files/

Rationale: This directory typically contains user-uploaded content such as images, documents, and other media that can be large in size and sensitive in nature. Storing this content in version control is inefficient and potentially risky as it can expose user data and significantly bloat the size of the repository. It's best practice to handle this content through backups and file storage solutions rather than through Git.


5. Private directory

Path: /sites/*/private/

Rationale: The private directory is used for files that are accessible through Drupal but must not be directly accessible via the web, such as private attachments and system files. Including this directory in the repository can risk exposing sensitive information. Managing access to these files through settings and server configurations outside of version control is a safer approach.


6. Temporary directory

Path: /sites/*/tmp/ or /tmp/

Rationale: Temporary files are used during file uploads, batch processing, and other transient operations. These files are not meant to be permanent and do not need to be tracked in version control. Storing temporary files in Git can lead to unnecessary repository growth and may include incomplete or transient data that has no relevance to the overall project.


How to manage these exclusions with .gitignore

The .gitignore file plays a critical role in maintaining a clean repository by specifying which files and directories Git should ignore. Here's how to configure your .gitignore for a Drupal 10 project:


.gitignore configuration

# Ignore directories managed by Composer

# Ignore sensitive directories

# Ignore specific files that may contain sensitive information

# Ignore other files and directories that should not be tracked

# Theme NPM token

# Ignore files generated by common IDEs

# Ignore .env files as they are personal

# Ignore other files


This setup ensures that your repository remains clean, containing only custom code and necessary configuration files that do not pose a risk if exposed.


3. Deployment strategy using composer

Follow these steps to deploy Drupal 10 using Composer, ensuring a smooth and stable setup:


Starting fresh - the initial setup

Create a New Project

Start by creating a new Drupal project using Composer

composer create-project drupal/recommended-project {my_project_name}

This command sets up Drupal with all recommended configurations.  Yes, replace {my_project_name} with your actual project name.


Add Contrib Modules

Use Composer to require any contributed modules or themes

composer require drupal/module_name


Ongoing development

Update Dependencies 

Regularly update your Composer dependencies to keep them secure and efficient

composer update --with-dependencies

Ensure this is done locally first, followed by thorough testing before pushing the changes.


Whether you're launching a new project from scratch or, like me, stepping into an established environment, transitioning towards best practices is crucial – especially when it comes to deployment.



Push Your Changes 

After testing your local changes, push the code related to your custom modules, themes, and configuration.


Run Composer Install on the Server 

After pulling the latest changes on your production server, run

composer install --no-interaction --prefer-dist --optimize-autoloader

This installs the exact versions of the dependencies that were tested locally, ensuring stability.  

The composer install command is a fundamental part of managing dependencies in PHP projects managed by Composer. It installs the necessary libraries and their dependencies as specified in the composer.lock file, ensuring your project has the exact versions needed to run as tested. The additional flags --no-interaction, --prefer-dist, and --optimize-autoloader enhance how this command functions.


This flag prevents Composer from asking any interactive questions during the installation process. It's particularly useful in automated environments, like continuous integration (CI) systems, where there's no user present to answer prompts. With --no-interaction, Composer will proceed with defaults or fail without waiting for user input, ensuring that scripts and deployments can run without manual intervention.


This flag instructs Composer to download the "dist" versions of the packages when available. The "dist" is typically a packaged, stable release such as a zip or tar file. It's generally faster to download than the "source" version, which might involve cloning a Git repository and retrieving its entire history. Using --prefer-dist can speed up installations and is often more efficient for bandwidth and caching, especially in a production or testing environment.


The autoloader is a critical component in PHP applications that dynamically loads PHP classes without the need for manual require statements. The --optimize-autoloader flag optimizes the autoloader by generating a class map, essentially creating a direct map of class names to file paths. This optimization significantly improves the performance of autoloading by reducing the filesystem checks and path resolutions needed when PHP loads classes. It's particularly beneficial in production environments, where performance is crucial, as it reduces the overhead on each request that involves class loading.

Together, these flags make the composer install command highly effective for automated, efficient, and performance-optimised deployments in various environments, from local development to production.

Adding a little more - such as the --no-dev flag to the composer install command further tailors the behaviour of Composer, especially in terms of which dependencies are installed.


The --no-dev flag instructs Composer to skip installing packages listed in the require-dev section of your composer.json file. The require-dev section typically includes packages that are needed for development purposes only, such as unit testing frameworks (e.g., PHPUnit), code quality tools (e.g., PHPStan, PHPCS), or other utilities that aren’t necessary to run the application in production environments.

Use Case: This flag is particularly useful for production deployments where you want to minimise the installation of unnecessary packages, thus reducing the size of your vendor directory and the potential attack surface of your application. By excluding development-only dependencies, you also decrease the time and bandwidth needed for deployments, enhancing deployment efficiency and performance.

Updating the command noted earlier – by combining this with your previous flags, the full command would be

composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev

When executed, Composer will install only the packages necessary for running the application, omitting any packages that are solely required for development. This makes the command ideal for setups where only production-ready code should be deployed, such as in a continuous integration/continuous deployment (CI/CD) pipeline for production environments.

Using --no-dev is a best practice for deploying PHP applications because it ensures that only essential code is deployed, helping to keep your production environments streamlined, secure, and efficient.


Wrapping it up

Deploying Drupal 10 using the outlined best practices ensures a clean, maintainable, and stable environment. By leveraging Composer and maintaining a clean repository, developers can minimise issues related to version conflicts and dependency management. This approach not only improves the deployment process but also enhances the overall security and performance of your Drupal site.

Related articles

Andrew Fletcher31 May 2024
Connecting AWS S3 with Docker for Drupal 10
Recently, I encountered an issue where my local Docker environment refused to connect to AWS S3, although everything worked seamlessly in AWS-managed environments. This challenge was not just a technical hurdle; it was a crucial bottleneck that needed resolution to ensure smooth Drupal deployments...