Skip to content

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 modifiedcomposer.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/ or vendor/ — these are generated; add them to .gitignore.
  • Installing Drush into core with composer require — it pollutes core's composer.json; use the core-dev add-on's wrappers instead.
  • Not running ddev poser after switching DRUPAL_CORE — the Drupal install must be rebuilt for the new version.
  • Using a global ddev-drupal-contrib install across projects with different core versions — always set DRUPAL_CORE per project.

See Also