DDEV out of the box is fast to set up but the defaults leave performance on the table. Mutagen file sync, custom services, project hooks, and shared team configuration in version control reduce friction for everyone on the project. I show the advanced DDEV setup I use on large Magento 2 projects with multiple developers.
Mutagen – faster file sync on macOS
# On macOS, Docker bind mounts are slow (NFS/osxfs overhead) # Mutagen creates a high-performance file sync between host and container # Enable in .ddev/config.yaml
# .ddev/config.yaml name: magento-shop type: magento2 php_version: "8.4" webserver_type: nginx-fpm docroot: pub database: type: mariadb version: "10.6" # Mutagen - dramatically faster on macOS (2-5x vs standard bind mounts) performance_mode: mutagen # Custom PHP config php_version: "8.4" upload_dirs: - pub/media - var # Xdebug mode (toggle with: ddev xdebug on|off) xdebug_enabled: false
# Performance comparison (PHP composer install): # Standard bind mount: 4m 32s # Mutagen: 1m 18s (3.5x faster) # Enable mutagen on existing project ddev config --performance-mode=mutagen ddev restart
Custom services – add OpenSearch, Mailpit, Redis Commander
# .ddev/docker-compose.opensearch.yaml
version: '3.6'
services:
opensearch:
image: opensearchproject/opensearch:2.13.0
environment:
- discovery.type=single-node
- DISABLE_SECURITY_PLUGIN=true
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- "9200"
labels:
com.ddev.site-name: ${DDEV_SITENAME}
com.ddev.approot: ${DDEV_APPROOT}
opensearch-dashboards:
image: opensearchproject/opensearch-dashboards:2.13.0
environment:
- OPENSEARCH_HOSTS=http://opensearch:9200
- DISABLE_SECURITY_DASHBOARDS_PLUGIN=true
ports:
- "5601:5601"
labels:
com.ddev.site-name: ${DDEV_SITENAME}
# .ddev/docker-compose.mailpit.yaml
# Catches all outgoing email in dev (replaces MailHog)
version: '3.6'
services:
mailpit:
image: axllent/mailpit:latest
ports:
- "8025:8025" # web UI
- "1025" # SMTP
labels:
com.ddev.site-name: ${DDEV_SITENAME}
# .ddev/docker-compose.redis-commander.yaml
version: '3.6'
services:
redis-commander:
image: rediscommander/redis-commander:latest
environment:
- REDIS_HOSTS=cache:redis:6379,session:redis-session:6379
ports:
- "8081:8081"
labels:
com.ddev.site-name: ${DDEV_SITENAME}
Custom DDEV commands for the team
#!/bin/bash
# .ddev/commands/web/magento-setup
## Description: Full Magento setup after fresh checkout
set -euo pipefail
echo "Installing Magento dependencies..."
composer install --no-interaction --prefer-dist
echo "Setting up Magento..."
bin/magento setup:install \
--base-url="https://${DDEV_SITENAME}.ddev.site/" \
--db-host=db --db-name=db --db-user=db --db-password=db \
--search-engine=opensearch \
--opensearch-host=opensearch --opensearch-port=9200 \
--admin-user=admin --admin-password=Admin123! \
--admin-firstname=Admin --admin-lastname=User \
--admin-email=admin@example.com \
--backend-frontname=admin \
--session-save=redis --session-save-redis-host=redis \
--cache-backend=redis --cache-backend-redis-server=redis \
--page-cache=redis --page-cache-redis-server=redis \
--page-cache-redis-db=1 \
--no-interaction
echo "Importing sample data..."
bin/magento sampledata:deploy
bin/magento setup:upgrade
bin/magento deploy:mode:set developer
echo "Done. Admin: https://${DDEV_SITENAME}.ddev.site/admin"
#!/bin/bash # .ddev/commands/web/m2 ## Description: Magento 2 shortcut (m2 cache:flush instead of bin/magento cache:flush) ## Usage: ddev m2 [magento command] bin/magento "$@"
Project hooks – automate repetitive tasks
# .ddev/config.yaml hooks
hooks:
post-start:
# Automatically configure Magento after ddev start
- exec: "bin/magento config:set web/secure/base_url https://${DDEV_SITENAME}.ddev.site/"
- exec: "bin/magento config:set web/unsecure/base_url http://${DDEV_SITENAME}.ddev.site/"
- exec: "bin/magento config:set catalog/search/opensearch_server_hostname opensearch"
- exec: "bin/magento cache:flush"
post-import-db:
# After any database import, anonymise and reconfigure
- exec: "php bin/anonymise.php"
- exec: "bin/magento config:set web/secure/base_url https://${DDEV_SITENAME}.ddev.site/"
- exec: "bin/magento cache:flush"
- exec: "bin/magento indexer:reindex"
pre-stop:
# Nothing needed, but available if cleanup required
Shared team configuration in version control
# What goes in version control (.ddev/ directory): git add .ddev/config.yaml # base config (no secrets) git add .ddev/docker-compose.*.yaml # custom services git add .ddev/commands/ # custom commands # What does NOT go in version control: echo ".ddev/.env" >> .gitignore # per-developer secrets echo ".ddev/config.*.yaml" >> .gitignore # personal overrides allowed # Personal overrides: .ddev/config.local.yaml (gitignored) # Developer can override php_version, xdebug settings, etc. # cat .ddev/config.local.yaml: # xdebug_enabled: true # php_version: "8.3" # testing on older PHP
Summary
Advanced DDEV setup removes the friction that accumulates in team development: Mutagen eliminates the macOS slowness penalty, custom service YAMLs give every developer the same stack with one command, project hooks automate configuration after start and database import, and custom commands standardise common tasks. Committing .ddev/ to version control means a new developer runs git clone + ddev start and has a fully configured environment in minutes.
