PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

DDEV advanced – mutagen, custom services, hooks, shared team configuration

by Henryk Tews / Thursday, 21 August 2025 / Published in Środowiska

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.

About Henryk Tews

What you can read next

Kubernetes for the PHP developer – kubectl debugging, Deployment YAML, HPA, troubleshooting
Docker from scratch – Dockerfile, nginx, docker-compose, Xdebug 3.x
Blackfire – DDEV setup, HTTP and CLI profiling, CI/CD assertions

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}