Drupal Contribution Environment: DDEV + the Workflow-Matched Add-on
When to Use
Use this when standing up a local environment for contribution work. DDEV is the community-selected standard (June 2024); the right add-on depends on which of the three workflows you are in.
Decision — Pick the Add-on for Your Workflow
| Add-on | For | Install command |
|---|---|---|
ddev/ddev-drupal-contrib (official) |
A single contrib module or theme | ddev add-on get ddev/ddev-drupal-contrib |
lussoluca/ddev-drupal-suite (community) |
Several interdependent contrib modules at once | ddev add-on get lussoluca/ddev-drupal-suite |
justafish/ddev-drupal-core-dev (community) |
Drupal core development | ddev get justafish/ddev-drupal-core-dev |
DDEV alternatives: Lando is a valid alternative (more flexible, for-profit governance, less Drupal-aligned); DrupalForge is cloud-hosted with zero local install. DDEV is the community standard — all Drupal.org contribution docs and the CI add-ons assume it.
Contrib Module/Theme Setup (ddev-drupal-contrib)
The key mental model: the module repo is the project root. web/ and vendor/ are generated by the add-on and .gitignore'd — never committed.
# 1. Clone the module repo (it IS the project root)
git clone git@git.drupalcode.org:project/my_module.git
cd my_module
# 2. Initialize DDEV
ddev config --project-type=drupal11 --docroot=web --php-version=8.3
# 3. Add the contrib add-on
ddev add-on get ddev/ddev-drupal-contrib
# 4. Start and install
ddev start
ddev poser # builds composer.contrib.json + installs Drupal into web/
ddev symlink-project # symlinks your module into web/modules/contrib/
# 5. Install Drupal
ddev drush si
After setup, ddev start automatically runs ddev symlink-project. Your module's own composer.json is never modified — composer.contrib.json isolates the Drupal install, which is what keeps CI honest.
Available Commands After Setup
ddev phpcs # phpcs at Drupal standards
ddev phpstan # phpstan with drupal extension
ddev phpunit web/modules/contrib/my_module/tests
ddev eslint
ddev stylelint
Multi-Version Testing
Switch the target Drupal core version without re-installing everything:
# Switch from D11 to D10 in .ddev/.env.web:
DRUPAL_CORE=^10
ddev restart # DDEV auto-selects a compatible PHP version
ddev poser # rebuilds with the new core version
Core Development Setup (ddev-drupal-core-dev)
# Clone core
git clone git@git.drupalcode.org:project/drupal.git
cd drupal
git checkout main
# Configure DDEV
ddev config --project-type=drupal11 --docroot=web
# Add the core-dev add-on (wraps core utilities; avoids polluting core's composer.json)
ddev get justafish/ddev-drupal-core-dev
ddev start
ddev drush si
# Run tests against core
ddev phpunit core/modules/<module>/tests
Do not install Drush into core with composer require — it pollutes core's composer.json and core CI will reject the change. Use the add-on's wrappers.
Multi-Module Development (ddev-drupal-suite)
ddev add-on get lussoluca/ddev-drupal-suite
ddev drupal-init 11 # initialize a D11 environment
ddev drupal-get-module my_module 11.x-1.x # add a module + branch
ddev-drupal-suite is community-maintained without official drupal.org endorsement. Recommended for multi-module scenarios; use ddev-drupal-contrib for single-module work.
Contributing to Someone Else's Module
Same as your own contrib setup — clone from your issue fork (see Issue Forks & MRs), use ddev-drupal-contrib, ddev poser, ddev symlink-project.
Common Mistakes
- Committing
web/orvendor/— these are generated; add them to.gitignore. - Installing Drush into core with
composer require— it pollutes core'scomposer.json; use the core-dev add-on's wrappers instead. - Not running
ddev poserafter switchingDRUPAL_CORE— the Drupal install must be rebuilt for the new version. - Using a global
ddev-drupal-contribinstall across projects with different core versions — always setDRUPAL_COREper project.