DDEV Development Expert You are an expert in DDEV, the Docker-based local development environment for PHP projects. Core Concepts DDEV provides a consistent, containerized local development environment with: Pre-configured PHP, web server, database containers Automatic HTTPS with mkcert Built-in Composer and Node.js support Easy multi-project management Note: Drush is NOT included by default - you must composer require drush/drush after creating a Drupal project. Essential Commands Project Management ddev start
Start project containers
ddev stop
Stop project containers
ddev restart
Restart containers
ddev poweroff
Stop all DDEV projects
ddev delete
Remove project (keeps files)
Executing Commands ddev drush < cmd
Run Drush commands
ddev composer < cmd
Run Composer
ddev php < script
Run PHP scripts
ddev exec < cmd
Run any command in web container
ddev ssh
SSH into web container
Database ddev mysql
MySQL CLI
ddev export-db
Export database
ddev import-db
Import database (--file=dump.sql)
ddev snapshot
Create database snapshot
ddev restore
Restore from snapshot
Utilities ddev describe
Show project info and URLs
ddev logs
View container logs
ddev launch
Open site in browser
ddev share
Create public URL (ngrok)
Configuration .ddev/config.yaml name : my - project type : drupal
Auto-detects Drupal version, or use drupal11/drupal10
docroot : web php_version : "8.3"
Use 8.3 for Drupal 11, 8.2 for Drupal 10
webserver_type : nginx - fpm database : type : mariadb version : "10.11"
Additional hostnames
additional_hostnames : - api.my - project.ddev.site
Extra PHP packages
webimage_extra_packages : [ php8.3 - imagick ] Common Customizations Custom services (.ddev/docker-compose.*.yaml): version : '3.6' services : redis : image : redis : 7 container_name : ddev - $ { DDEV_SITENAME } - redis labels : com.ddev.site-name : $ { DDEV_SITENAME } expose : - "6379" PHP overrides (.ddev/php/my-settings.ini): memory_limit = 512M upload_max_filesize = 64M post_max_size = 64M Nginx config (.ddev/nginx_full/nginx-site.conf): Custom nginx configuration for special routing needs. Drupal-Specific Setup New Drupal 11 Project mkdir my-drupal && cd my-drupal ddev config --project-type = drupal --docroot = web --php-version = 8.3 ddev start ddev composer create-project drupal/recommended-project:^11 ddev composer require drush/drush ddev drush site:install --account-name = admin --account-pass = admin -y ddev launch Important notes: ddev composer create-project requires a clean directory - move any existing files (like .claude/ ) out first, then move them back after Drush is NOT included in Drupal 11's recommended-project - always install it separately Use --project-type=drupal (auto-detects version) or explicitly drupal11 New Drupal 10 Project mkdir my-drupal && cd my-drupal ddev config --project-type = drupal --docroot = web --php-version = 8.2 ddev start ddev composer create-project drupal/recommended-project:^10 ddev composer require drush/drush ddev drush site:install --account-name = admin --account-pass = admin -y ddev launch Existing Drupal Project cd existing-project ddev config --project-type = drupal --docroot = web ddev start ddev composer install ddev import-db --file = database.sql.gz ddev drush cr Troubleshooting Common Issues ddev composer create-project fails with "not allowed to be present":
This happens when extra directories exist (like .claude/, .git/, etc.)
Solution: Move them out temporarily
mv .claude /tmp/claude-backup mv .git /tmp/git-backup ddev composer create-project drupal/recommended-project:^11 mv /tmp/claude-backup .claude mv /tmp/git-backup .git Port conflicts: ddev poweroff
Check what's using ports 80/443
sudo lsof -i :80 Container issues: ddev restart ddev debug refresh
Rebuild containers
ddev delete && ddev start
Nuclear option
Database connection issues: Host: db (inside container) or 127.0.0.1:PORT (outside) Check port with ddev describe Permission issues: ddev exec chown -R $( id -u ) : $( id -g ) . Useful Debug Commands ddev debug capabilities
Show DDEV capabilities
ddev debug router
Show router status
ddev logs -f
Follow logs
ddev exec env
Show environment variables
Multi-Environment Workflows Using ddev pull Configure providers in .ddev/providers/:
.ddev/providers/platform.yaml
environment_variables : project : my - project environment : main db_pull_command : command : platform db : dump - e $ { environment } Then: ddev pull platform Xdebug Configuration Enable Xdebug ddev xdebug on
Enable step debugging
ddev xdebug off
Disable (faster performance)
ddev xdebug status
Check current state
IDE Configuration
VS Code
(with PHP Debug extension):
// .vscode/launch.json
{
"version"
:
"0.2.0"
,
"configurations"
:
[
{
"name"
:
"Listen for Xdebug"
,
"type"
:
"php"
,
"request"
:
"launch"
,
"port"
:
9003
,
"pathMappings"
:
{
"/var/www/html"
:
"${workspaceFolder}"
}
}
]
}
PHPStorm
:
Settings → PHP → Servers
Add server: name matches DDEV project name
Host:
.ddev/php/xdebug.ini
[ xdebug ] xdebug.mode = debug,develop,coverage Modes: debug (step debugging), develop (enhanced errors), coverage (code coverage), profile (profiling) Custom Services Redis
.ddev/docker-compose.redis.yaml
services : redis : image : redis : 7 - alpine container_name : ddev - $ { DDEV_SITENAME } - redis labels : com.ddev.site-name : $ { DDEV_SITENAME } com.ddev.approot : $DDEV_APPROOT expose : - "6379" volumes : - redis - data : /data volumes : redis-data : Drupal settings.php: $settings [ 'redis.connection' ] [ 'host' ] = 'redis' ; $settings [ 'redis.connection' ] [ 'port' ] = 6379 ; $settings [ 'cache' ] [ 'default' ] = 'cache.backend.redis' ; Solr
.ddev/docker-compose.solr.yaml
services
:
solr
:
image
:
solr
:
9
container_name
:
ddev
-
$
{
DDEV_SITENAME
}
-
solr
labels
:
com.ddev.site-name
:
$
{
DDEV_SITENAME
}
com.ddev.approot
:
$DDEV_APPROOT
expose
:
-
"8983"
volumes
:
-
solr
-
data
:
/var/solr
command
:
solr
-
precreate drupal
volumes
:
solr-data
:
Access Solr:
ddev describe
shows URL, typically
https://
.ddev/docker-compose.elasticsearch.yaml
services : elasticsearch : image : elasticsearch : 8.11.0 container_name : ddev - $ { DDEV_SITENAME } - elasticsearch labels : com.ddev.site-name : $ { DDEV_SITENAME } com.ddev.approot : $DDEV_APPROOT environment : - discovery.type=single - node - xpack.security.enabled=false - "ES_JAVA_OPTS=-Xms512m -Xmx512m" expose : - "9200" volumes : - elasticsearch - data : /usr/share/elasticsearch/data volumes : elasticsearch-data : Mailpit (Email Testing) DDEV includes Mailpit by default: ddev launch -m
Open Mailpit UI
All outgoing mail is captured at
https://
Enable globally
ddev config global --mutagen-enabled
Or per-project in .ddev/config.yaml
mutagen_enabled: true When to use Mutagen: macOS with large codebases (vendor, node_modules) Windows with WSL2 Projects with slow file I/O Mutagen commands: ddev mutagen status
Check sync status
ddev mutagen sync
Force sync
ddev mutagen reset
Reset if issues
NFS (macOS alternative) For macOS without Mutagen: ddev config global --nfs-mount-enabled Performance Tips Exclude unnecessary files from sync:
.ddev/config.yaml
upload_dirs : - sites/default/files Use tmpfs for temp files :
.ddev/docker-compose.performance.yaml
services : web : tmpfs : - /tmp Increase PHP memory for large operations :
.ddev/php/performance.ini
memory_limit
1024M Custom DDEV Commands Create project-specific commands in .ddev/commands/ :
.ddev/commands/web/refresh
!/bin/bash
Description: Full site refresh (db + config + cache)
Usage: refresh
Example: ddev refresh
set -e echo "Importing database..." drush sql:drop -y drush sql:cli < /var/www/html/reference.sql echo "Importing config..." drush config:import -y echo "Running updates..." drush updatedb -y echo "Clearing cache..." drush cache:rebuild echo "Done!" Make executable: chmod +x .ddev/commands/web/refresh Then run: ddev refresh Command Locations .ddev/commands/web/ - Run in web container .ddev/commands/host/ - Run on host machine .ddev/commands/db/ - Run in database container CI/CD Integration GitHub Actions
.github/workflows/test.yml
name : Tests on : [ push , pull_request ] jobs : test : runs-on : ubuntu - latest steps : - uses : actions/checkout@v4 - name : Setup DDEV uses : ddev/github - action - setup - ddev@v1 - name : Start DDEV run : ddev start - name : Install dependencies run : ddev composer install - name : Run tests run : ddev exec ./vendor/bin/phpunit GitLab CI
.gitlab-ci.yml
test : image : ddev/ddev - gitpod - base : latest services : - docker : dind variables : DOCKER_HOST : tcp : //docker : 2375 script : - ddev start - ddev composer install - ddev exec ./vendor/bin/phpunit Best Practices Commit .ddev folder (except .ddev/db_snapshots, .ddev/.gitignore handles this) Use .ddev/config.local.yaml for personal overrides (gitignored) Document custom services in project README Use snapshots before risky database operations Keep DDEV updated : ddev self-upgrade Use Mutagen on macOS/Windows for better performance Create custom commands for repetitive tasks Test DDEV config in CI to catch issues early