Composer
Composer is a dependency manager for PHP projects, including Drupal. It allows you to declare the libraries your project depends on and manages them for you.
This page covers the day-to-day workflows: requiring, updating, patching,
and auditing packages. It ends with a key-by-key reference of the shipped
composer.json file.
Installing
To install packages, use composer require to include the package and
resolve dependencies.
- Ahoy
- Docker Compose
ahoy composer require drupal/devel
docker compose exec cli composer require drupal/devel
By default, stable releases are installed. If you need a non-stable version
(for example, alpha, beta, RC), specify the version constraint
explicitly:
- Ahoy
- Docker Compose
ahoy composer require drupal/devel:^1.0.0@beta
docker compose exec cli composer require drupal/devel:^1.0.0@beta
Make sure that the minimum-stability setting in composer.json is set to
the version constraint you need. For example, to allow alpha, beta, and
RC versions:
{
"minimum-stability": "beta"
}
Adding JavaScript/CSS libraries (npm packages)
To install JavaScript or CSS libraries as Drupal libraries with Composer,
they must be defined as inline Composer
packages. The
package type for inline packages must be drupal-library, as Drupal treats
these as libraries and places them into the web/libraries/ directory where
it expects to find all libraries.
Don't use Asset Packagist to install front-end libraries. Drupal.org maintains the distribution of Composer packages with established security policies. Asset Packagist, by contrast, is a third-party service that lacks the safeguards needed to protect against supply chain attacks. Instead, define packages as inline Composer packages as shown below.
-
Define the package in
composer.jsonunder therepositoriessection:{"repositories": [{"type": "package","package": {"name": "gdsmith/jquery.easing","type": "drupal-library","version": "1.4.1","source": {"type": "git","url": "https://github.com/gdsmith/jquery.easing","reference": "1.4.1"}}}]} -
Require the package using Composer:
- Ahoy
- Docker Compose
ahoy composer require gdsmith/jquery.easing
docker compose exec cli composer require gdsmith/jquery.easing
Updating
To update all dependencies:
- Ahoy
- Docker Compose
ahoy composer update
docker compose exec cli composer update
If your project uses patches to modify dependencies, the update may fail if the patches aren't compatible with the new versions of the dependencies.
A common solution is to remove the patches temporarily, run the update, and then reapply the patches one by one.
To update a specific package and its dependencies:
- Ahoy
- Docker Compose
ahoy composer update vendor/package-name --with-dependencies
docker compose exec cli composer update vendor/package-name \
--with-dependencies
For updating Drupal core, use:
- Ahoy
- Docker Compose
ahoy composer update "drupal/core-*" --with-dependencies
docker compose exec cli composer update "drupal/core-*" \
--with-dependencies
After updating core, review changes with git diff, especially modified
scaffolding files like .htaccess, and commit them in a single commit.
Dependency bumping
Dependency bumping updates
version constraints in your composer.json to match currently installed
versions. This prevents accidental downgrades when adding new dependencies
and improves dependency resolution performance.
Vortex enables this automatically via the bump-after-update
configuration. Every time you run composer update, your version
constraints are updated to reflect the installed versions.
Vortex enables automatic bumping because it's designed for application projects (Drupal sites) where you control the entire dependency tree.
This approach isn't recommended for libraries (reusable packages). Libraries
should keep version constraints as broad as possible to avoid
dependency hell for
downstream users. If you're building a library, use
bump-after-update: "dev" to only bump development dependencies, or disable
automatic bumping and run composer bump --dev-only manually.
Manual bumping
# Bump all dependencies
composer bump
# Bump specific package
composer bump drupal/core-recommended
# Bump only development dependencies
composer bump --dev-only
# Preview changes without modifying files
composer bump --dry-run
Overriding paths
To override package installation paths, modify composer.json:
{
"extra": {
"installer-paths": {
"web/libraries/jquery-easing": [
"gdsmith/jquery.easing"
],
"web/libraries/{$name}": [
"type:drupal-library"
]
}
}
}
The first entry overrides the path for a specific package, placing it into a
custom directory name. The second entry is a catch-all that routes all
drupal-library type packages to web/libraries/{$name} by default.
Vortex tooling package
Vortex ships its operational scripts (build, provision, deployment, and
notification tooling) as the
drevops/vortex-tooling
Composer package. The package is installed to
vendor/drevops/vortex-tooling/src/ and exposes its entry-point scripts as
Composer binaries under vendor/bin/vortex-*, such as
vendor/bin/vortex-provision or vendor/bin/vortex-fetch-db.
See Architecture > Automation scripts for how the scripts are structured and how the router scripts dispatch to the provider-specific logic.
The package is required in composer.json with a tilde constraint:
{
"require": {
"drevops/vortex-tooling": "~1.4.0"
}
}
The ~ constraint accepts patch releases but holds the minor version, so a
new minor release can't reach your deployments until you raise the
constraint yourself.
To update the package within the constraint:
- Ahoy
- Docker Compose
ahoy composer update drevops/vortex-tooling
docker compose exec cli composer update drevops/vortex-tooling
To move to a new minor release, raise the constraint in composer.json
first and then run the update.
Don't modify files under vendor/drevops/vortex-tooling/src/, because the
changes are lost on the next composer update. Customize the scripts
through their environment variables or add your own scripts
under scripts/ (for example,
custom provision scripts). When
neither is enough, apply a patch so the change survives
dependency updates.
Patching
Vortex uses
cweagans/composer-patches
v2.x for applying patches to Composer dependencies. Version 2.x uses
git-based patching with git apply for better cross-platform consistency.
For official documentation, visit: Composer Patches Recommended Workflows
Understanding patches.lock.json
Composer Patches v2.x automatically generates a patches.lock.json file
that contains:
- Patch metadata (URLs, descriptions, target packages)
- SHA-256 checksums for patch verification
This file must be committed to version control (like composer.lock).
Committing it makes builds reproducible across teams and CI/CD environments.
The patch state stays explicit and trackable, and the checksums in the file
verify patch integrity.
Adding a new patch
-
Define the patch in
composer.jsonunderextra.patches:"extra": {"patches": {"drupal/foobar": {"Fix for issue #123": "https://www.drupal.org/files/issues/fix-123.patch"}}} -
Regenerate
patches.lock.json:
- Ahoy
- Docker Compose
ahoy composer patches-relock
docker compose exec cli composer patches-relock
- Remove and reinstall patched packages:
- Ahoy
- Docker Compose
ahoy composer patches-repatch
docker compose exec cli composer patches-repatch
composer patches-repatch removes patched dependencies from vendor/ and
reinstalls them. Ensure you have no unsaved changes in those directories.
- Update
composer.lock:
- Ahoy
- Docker Compose
ahoy composer update --lock
docker compose exec cli composer update --lock
Removing a patch
-
Delete the patch definition from
composer.json -
Regenerate
patches.lock.json:
- Ahoy
- Docker Compose
ahoy composer patches-relock
docker compose exec cli composer patches-relock
- Manually delete the affected dependency:
- Ahoy
- Docker Compose
ahoy cli rm -rf vendor/drupal/foobar
docker compose exec cli rm -rf vendor/drupal/foobar
- Reinstall without the patch:
- Ahoy
- Docker Compose
ahoy composer patches-repatch
docker compose exec cli composer patches-repatch
- Update
composer.lock:
- Ahoy
- Docker Compose
ahoy composer update --lock
docker compose exec cli composer update --lock
Security auditing
composer audit checks the installed packages against published security
advisories and reports abandoned packages. Vortex configures Composer's
config.policy so that advisories never block composer install,
composer update, or composer require. The composer audit command still
fails when an advisory is found, so vulnerabilities stay visible locally and
in CI.
➡️ See Dependency audit for the full policy reference, the audit commands, and ignoring assessed advisories.
composer.json reference
File overview
Vortex comes with a pre-configured composer.json that lists the
dependencies a Drupal project needs, along with development tools to help
you maintain your code quality.
The composer.json file is the core configuration file for Composer,
detailing your project's dependencies, scripts, and settings.
This section describes each part of the composer.json file and its role.
Expand to see composer.json
{
"name": "your_org/your_site",
"description": "Drupal 11 implementation of YOURSITE for YOURORG",
"license": "proprietary",
"type": "project",
"require": {
"php": ">=8.4",
"composer/installers": "^2.3",
"cweagans/composer-patches": "^2.0",
"drevops/vortex-tooling": "~1.4.0",
"drupal/clamav": "^2.1",
"drupal/coffee": "^2.0.1",
"drupal/config_split": "^2.0.2",
"drupal/config_update": "^2@alpha",
"drupal/core-composer-scaffold": "~11.4.9",
"drupal/core-recommended": "~11.4.9",
"drupal/devel": "^5.5",
"drupal/drupal_helpers": "^2.1.1",
"drupal/environment_indicator": "^4.0.25",
"drupal/fast_404": "^3.7",
"drupal/generated_content": "^2.1.2",
"drupal/migrate_plus": "^6.0.10",
"drupal/migrate_tools": "^6.1.4",
"drupal/navigation_extra_tools": "^1.3.2",
"drupal/pathauto": "^1.15.0",
"drupal/redirect": "^1.13.0",
"drupal/redis": "^1.11",
"drupal/reroute_email": "^2.3@RC",
"drupal/robotstxt": "^1.6",
"drupal/sdc_devel": "^1.0.3",
"drupal/search_api": "^1.42.0",
"drupal/search_api_solr": "^4.4.0",
"drupal/seckit": "^2.0.3",
"drupal/shield": "^1.8",
"drupal/stage_file_proxy": "^4.0.0",
"drupal/storybook": "^1.0.4",
"drupal/testmode": "^2.7.2",
"drupal/xmlsitemap": "^2.0",
"drush/drush": "^13.8.0"
},
"require-dev": {
"behat/behat": "^3.34.0",
"dantleech/gherkin-lint": "^0.2.4",
"drevops/behat-screenshot": "^2.6.0",
"drevops/behat-steps": "^3.14.4",
"drevops/phpcs-standard": "^1.1.0",
"drupal/coder": "^9.0.1",
"drupal/drupal-extension": "^6.1",
"ergebnis/composer-normalize": "^2.54.0",
"mglaman/phpstan-drupal": "^2.2.2",
"mikey179/vfsstream": "^1.6.12",
"palantirnet/drupal-rector": "^1.1.3",
"phpcompatibility/php-compatibility": "^10.0@alpha",
"phpspec/prophecy-phpunit": "^2.5",
"phpstan/extension-installer": "^1.4.3",
"phpstan/phpstan": "^2.3.1",
"phpunit/phpunit": "^11.5.57",
"pyrech/composer-changelogs": "^2.2",
"softcreatr/jsonpath": "^0.10 || ^1.0",
"vincentlanglet/twig-cs-fixer": "^4.1.1"
},
"conflict": {
"drupal/drupal": "*"
},
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
},
{
"type": "path",
"url": ".vortex/tooling",
"options": {
"versions": {
"drevops/vortex-tooling": "1.4.0"
}
}
}
],
"minimum-stability": "stable",
"prefer-stable": true,
"autoload-dev": {
"classmap": [
"tests/phpunit/"
]
},
"config": {
"allow-plugins": {
"composer/installers": true,
"cweagans/composer-patches": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"drupal/core-composer-scaffold": true,
"ergebnis/composer-normalize": true,
"php-http/discovery": true,
"phpstan/extension-installer": true,
"pyrech/composer-changelogs": true,
"symfony/runtime": true,
"tbachert/spi": true
},
"bump-after-update": true,
"discard-changes": true,
"platform": {
"php": "8.4.23"
},
"policy": {
"advisories": {
"block": false,
"audit": "fail"
},
"abandoned": {
"audit": "report"
}
},
"sort-packages": true
},
"extra": {
"drupal-scaffold": {
"file-mapping": {
"[project-root]/.editorconfig": false,
"[project-root]/.gitattributes": false,
"[web-root]/.csslintrc": false,
"[web-root]/.eslintignore": false,
"[web-root]/.eslintrc.json": false,
"[web-root]/.ht.router.php": false,
"[web-root]/.htaccess": false,
"[web-root]/INSTALL.txt": false,
"[web-root]/README.md": false,
"[web-root]/example.gitignore": false,
"[web-root]/robots.txt": false,
"[web-root]/sites/example.settings.local.php": false,
"[web-root]/sites/example.sites.php": false,
"[web-root]/update.php": false
},
"locations": {
"web-root": "web/"
}
},
"installer-paths": {
"web/core": [
"type:drupal-core"
],
"web/libraries/{$name}": [
"type:drupal-library"
],
"web/modules/contrib/{$name}": [
"type:drupal-module"
],
"web/profiles/contrib/{$name}": [
"type:drupal-profile"
],
"recipes/{$name}": [
"type:drupal-recipe"
],
"web/themes/contrib/{$name}": [
"type:drupal-theme"
],
"drush/Commands/contrib/{$name}": [
"type:drupal-drush"
],
"web/modules/custom/{$name}": [
"type:drupal-custom-module"
],
"web/profiles/custom/{$name}": [
"type:drupal-custom-profile"
],
"web/themes/custom/{$name}": [
"type:drupal-custom-theme"
]
},
"patchLevel": {
"drupal/core": "-p2"
},
"patches": {}
}
}
name
name is a
unique identifier for the project in Composer's ecosystem consisting of a
vendor name and the project's name.
description
A brief summary of the project's purpose.
type
type specifies the type of the package. It tells Composer and any systems
integrating with Composer how to treat the package, and it can influence how
the package is installed and used.
license
license indicates the
license of the project. Since Vortex is a template for your consumer
sites that are usually proprietary, we use proprietary as the default
value. More license identifiers are listed at the SPDX Open Source License
Registry.
repositories
The repositories
section defines custom package repositories, needed for accessing packages
outside the default Packagist repository.
| Repository | Type | Description |
|---|---|---|
packages.drupal.org/8 | composer | The official source for Drupal modules, themes, and distributions, which aren't published on Packagist. |
.vortex/tooling | path | Resolves the in-tree tooling package while developing Vortex itself. The installer removes this entry during site creation, so your project installs drevops/vortex-tooling from Packagist instead. |
require
The require section
specifies the packages and libraries your project needs.
The Documentation column links to the page covering that package in more depth. A Settings link means Vortex ships a per-module override file that applies environment-aware defaults for that module. Some modules still need a running service or environment-specific values on top of those defaults.
➡️ See Contributed modules for what each override actually configures and where each module is installed.
| Package | Description | Documentation |
|---|---|---|
php | The minimum PHP version required to run this project. Specify a range such as >=8.4 rather than an exact version such as 8.4.0. | |
composer/installers | Installs packages into the correct location based on their package type, such as drupal-module, drupal-theme, or drupal-profile. | |
cweagans/composer-patches | Patches Composer packages with git apply, incorporating fixes that aren't yet in an official release. Patch metadata and SHA-256 checksums are recorded in patches.lock.json for reproducible builds. | Patching |
drevops/vortex-tooling | Ships the Vortex operational scripts (build, provision, deployment, and notification tooling) that your site runs from vendor/drevops/vortex-tooling/src/. | Vortex tooling package |
drupal/clamav | Scans uploaded files for malware with the ClamAV engine before they're saved. | Settings |
drupal/coffee | Adds a keyboard shortcut to jump straight to any administration page by typing its name. | |
drupal/config_split | Splits configuration into sets that are conditionally imported, enabling environment-specific configuration such as development-only modules. | Settings |
drupal/config_update | Provides tools and Drush commands to report, revert, and import configuration changes relative to the defaults shipped by modules. | |
drupal/core-composer-scaffold | Downloads Drupal Scaffold files such as index.php and update.php from the drupal/core project and places them inside the web root. | |
drupal/core-recommended | Pins the set of dependency versions that the Drupal community tests and recommends for a particular core version. | |
drupal/devel | A suite of development tools for inspecting variables, entities, and the service container while debugging. | Settings |
drupal/drupal_helpers | A collection of helper functions that simplify writing update hooks and deployment operations. | Drupal helpers |
drupal/environment_indicator | Shows a colored banner identifying the current environment to prevent accidental changes on the wrong site. | Settings |
drupal/fast_404 | Answers requests for missing files with a 404 before Drupal bootstraps, saving the bootstrap that core's own fast 404 still performs. | Settings |
drupal/generated_content | Generates deterministic placeholder content from declarative definitions for development and testing. | Generated Content, Settings |
drupal/migrate_plus | Extends the core Migrate API with extra source and process plugins and configuration-entity migrations. | Migrations |
drupal/migrate_tools | Provides Drush commands and a UI to run, roll back, and monitor migrations. | Migrations |
drupal/navigation_extra_tools | Adds administration shortcuts (clear caches, run cron, run database updates) to the core Navigation module. | |
drupal/pathauto | Automatically generates URL aliases for content based on configurable patterns. | |
drupal/redirect | Manages URL redirects and creates them automatically when content URLs change. | |
drupal/redis | Integrates Drupal with the Redis in-memory store for cache and lock backends. | Settings |
drupal/reroute_email | Reroutes all outbound email to a configured address on non-production environments to avoid emailing real users. | Settings |
drupal/robotstxt | Manages the robots.txt file from the admin UI, useful when it can't be placed on disk (for example, in multisite setups). | Settings |
drupal/sdc_devel | Development and validation tooling for Single Directory Components (SDC), reporting issues in component definitions and templates. | |
drupal/search_api | Provides a framework for building search experiences with pluggable indexing backends. | |
drupal/search_api_solr | A Search API backend that integrates Apache Solr for fast, scalable search. | |
drupal/seckit | Adds configurable security-hardening HTTP headers, including Content Security Policy and anti-framing protection. | Settings |
drupal/shield | Protects non-production environments behind HTTP basic authentication. | Settings |
drupal/stage_file_proxy | Fetches media files from a remote site on demand, so local environments don't need a full copy of the files directory. | Settings |
drupal/testmode | Adjusts site behavior during automated tests, for example by filtering out generated content from listings. | Testmode, Settings |
drupal/xmlsitemap | Generates a multilingual XML sitemap to help search engines index the site. | Settings |
drush/drush | A command-line shell and scripting interface for Drupal, providing a wide range of utilities to manage and interact with your Drupal sites. | Drush |
require-dev
The require-dev
section lists packages used for development purposes, like code quality
checks and testing. These tools are needed during development but not in
production environments, so the production deployment stays smaller.
| Package | Description | Documentation |
|---|---|---|
behat/behat | A PHP framework for Behavior-Driven Development (BDD), running human-readable stories that describe the behavior of your application. It facilitates communication between developers, stakeholders, and clients. | Behat |
dantleech/gherkin-lint | Lints the Gherkin feature files used in Behat tests, checking syntax and formatting for consistency. | Gherkin Lint |
drevops/behat-screenshot | Automatically captures a screenshot when a Behat scenario fails, which helps to understand why a test failed. | Behat |
drevops/behat-steps | A collection of pre-defined Behat step definitions for Drupal, speeding up the process of writing new tests. | Behat |
drevops/phpcs-standard | A PHP_CodeSniffer coding standard that extends the Drupal coding standards with additional rules and best practices. | PHPCS |
drupal/coder | Provides the PHP_CodeSniffer rules for the Drupal coding standards. The 9.x line targets PHP_CodeSniffer 4.x. | PHPCS |
drupal/drupal-extension | A Behat extension that integrates with Drupal, offering Drupal-specific step definitions and bootstrapping the site for testing. | Behat |
ergebnis/composer-normalize | A Composer plugin that normalizes the formatting and key order of composer.json. | |
mglaman/phpstan-drupal | Teaches PHPStan about Drupal APIs, so static analysis understands hooks, services, and entities in modules and themes. | PHPStan |
mikey179/vfsstream | A virtual file system for PHPUnit tests, allowing file operations to be exercised without touching the real file system. | PHPUnit |
palantirnet/drupal-rector | Rector rules that rewrite deprecated Drupal code, making core upgrades more efficient. It installs rector/rector and constrains it to the versions its rule sets support. | Rector |
phpcompatibility/php-compatibility | PHP_CodeSniffer sniffs that check code for compatibility with specific PHP versions. The 10.x line targets PHP_CodeSniffer 4.x. | PHPCS |
phpspec/prophecy-phpunit | Integrates the Prophecy mocking library with PHPUnit to provide advanced mocking capabilities in tests. | PHPUnit |
phpstan/extension-installer | Automatically registers the installed PHPStan extensions, removing the need to wire them up by hand. | PHPStan |
phpstan/phpstan | A static analysis tool that finds type errors, incorrect method calls, and other bugs without running the code. | PHPStan |
phpunit/phpunit | The PHP testing framework, used here for unit, kernel, and functional tests. | PHPUnit |
pyrech/composer-changelogs | Prints a summary of package additions, updates, and removals after running composer update. | |
softcreatr/jsonpath | A JSONPath implementation required by the drevops/behat-steps JSON assertion steps. | Behat |
vincentlanglet/twig-cs-fixer | Checks and fixes Twig templates against a coding standard. | Twig CS Fixer |
conflict
The conflict section
prevents installation conflicts with standalone Drupal core, so core
versions don't clash and core files stay consistent.
minimum-stability
The
minimum-stability
setting controls the minimum stability level of the packages that Composer
is allowed to install. By setting it to "stable", you are instructing
Composer to prefer stable versions of packages over their unstable (like
beta or alpha) versions.
prefer-stable
The
prefer-stable
setting, when set to true, instructs Composer to prefer stable versions of
packages even when the minimum-stability setting allows less stable
versions. This is useful when some dependencies don't have a stable release
yet, or when a newer feature or fix is only available in a less stable
version.
config
The config section
configures Composer's behavior in the project.
allow-plugins: This setting specifies which Composer plugins are allowed to run. It's a security measure to prevent the execution of untrusted code from third-party plugins. Each plugin needs to be explicitly allowed to ensure it can execute.policy: Introduced in Composer 2.10.0, this unified setting controls security auditing and version blocking of dependencies. With the Vortex configuration, advisories never block installs but still failcomposer audit, so vulnerabilities stay visible without breaking reproducible builds. ➡️ See Security auditing for the option values, ignoring advisories, and the CI integration.bump-after-update: Automatically updates version constraints incomposer.jsonto match currently installed package versions after runningcomposer update. Vortex sets this totrueto bump all dependencies. Available values:true(all dependencies),false(disabled),"dev"(only dev dependencies), or"no-dev"(only non-dev dependencies).discard-changes: When set totrue, any local changes made to the dependencies (packages under version control like Git) are discarded without prompting when you runcomposer installorcomposer update. Composer overwrites the local changes with the data from the source repository.platform: Specifies the PHP version of the platform environment where the current project runs. This should be specified as an exact version number (for example,8.4.23). It should be kept in sync with thephpversion in the Docker containers.sort-packages: When set totrue, Composer keeps packages sorted incomposer.jsonandcomposer.lock. Sorted files are more readable and cause fewer merge conflicts in version control.
autoload-dev
The autoload-dev
section defines how Composer automatically loads PHP
development-specific classes within the project, without needing to manually
include or require each class file.
extra
The extra section holds
custom configuration for packages. These packages read settings from this
section to tailor their behavior to the needs and structure of your Drupal
project.
drupal-scaffold: This setting controls which files should be scaffolded:locations: Specifies the location of the web root (the directory containing theindex.phpfile).file-mapping: Determines which files are managed by the scaffold process. Vortex comes with sensible defaults, but you can customize this section to suit your needs.
installer-paths: Defines custom installation paths for package types like Drupal modules, themes, and libraries.patchLevel: Defines the patch level for specific packages, in this case,drupal/core. The-poption followed by a number (for example,-p1,-p2) in patch commands specifies the number of leading directories to strip from the file paths found in the patch file. This determines how the paths in the patch file are interpreted relative to the current directory where the patch is being applied.patches: Specifies the patches to be applied to specific packages. The applied patch state lives in the generatedpatches.lock.jsonfile, which is committed likecomposer.lock. ➡️ See Patching