
Drupal Ddev
- 218 installs
- 73 repo stars
- Updated July 30, 2026
- grasmash/drupal-claude-skills
Drupal DDEV is an agent skill that documents complete DDEV config.yaml settings for local Drupal 9–11 development.
About
Drupal DDEV is an agent skill that gives solo and indie Drupal builders a complete reference for DDEV’s config.yaml so local environments match production constraints without memorizing every key. It spans project identity (name, type, docroot), PHP and database versions aligned with Drupal core requirements, webserver choice, Composer and Node pinning, router ports, environment variables, upload directory mounts, and lifecycle hooks such as cache rebuilds after container start. Multi-phase use is natural: in Validate you stand up a prototype site quickly; in Build you iterate modules and themes against MariaDB and nginx-fpm; in Operate you mirror timezone, NFS, and performance toggles when debugging “works on my machine” issues. The skill is procedural reference material—ideal when your agent drafts or audits a .ddev folder before you run ddev start. It does not replace Drupal coding skills but removes friction from the container layer so you ship CMS features faster on Claude Code, Cursor, or Codex.
- Full .ddev/config.yaml reference with Drupal 9/10/11 project types
- Covers PHP 8.1+, MariaDB versions, nginx-fpm, Composer, and Node toolchains
- Documents hooks (e.g. post-start drush cr), upload_dirs, and web_environment vars
- Includes performance_mode (mutagen), router ports, and additional hostnames/FQDNs
- Explains naming, docroot (web), and omit_containers / additional_services patterns
Drupal Ddev by the numbers
- 218 all-time installs (skills.sh)
- Ranked #388 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 1, 2026 (Skillselion catalog sync)
npx skills add https://github.com/grasmash/drupal-claude-skills --skill drupal-ddevAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 218 |
|---|---|
| repo stars | ★ 73 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 30, 2026 |
| Repository | grasmash/drupal-claude-skills ↗ |
What it does
Configure and standardize local Drupal stacks with DDEV so you can develop and test without fighting PHP, database, and webserver versions.
Who is it for?
Drupal maintainers standardizing ddev config for drupal10/drupal11 projects on one machine.
Skip if: Skip if you're on non-DDEV stacks only (Lando-only, pure Docker Compose without DDEV) or frontend-only Jamstack sites with no PHP docroot.
When should I use this skill?
You are creating, migrating, or reviewing a Drupal project’s DDEV configuration.
What you get
You get a validated .ddev/config.yaml pattern—hooks, env vars, and versions included—so ddev start yields a consistent Drupal backend you can build and debug against.
- .ddev/config.yaml snippets or full reference-aligned config
- Hook and environment variable recommendations
By the numbers
- Documents drupal9, drupal10, and drupal11 DDEV project types
- Example stack includes PHP 8.3, MariaDB 10.6, nginx-fpm, Node 20
Files
DDEV for Drupal Development
Comprehensive patterns for using DDEV as your local Drupal development environment, including setup, configuration, workflow optimization, and troubleshooting.
When This Skill Activates
Activates when working with DDEV local development including:
- DDEV configuration (.ddev/config.yaml)
- Local environment setup and management
- Database import/export operations
- Drush integration
- Xdebug and debugging tools
- Performance optimization
- Multi-site and custom commands
---
Available Topics
Core Setup
- @references/installation.md - Installing and configuring DDEV
- @references/config-yaml.md - .ddev/config.yaml reference
- @references/commands.md - Essential DDEV commands
Database Operations
- @references/database.md - Import, export, and snapshot workflows
- @references/drush.md - Using Drush with DDEV
Development Tools
- @references/xdebug.md - Debugging with Xdebug
- @references/mailhog.md - Email testing with MailHog
- @references/solr.md - Local Solr search setup
Advanced
- @references/custom-commands.md - Creating project-specific commands
- @references/hooks.md - Pre/post hooks automation
- @references/performance.md - Optimizing DDEV performance
- @references/multisite.md - Multi-site configuration
See /references/ directory for complete documentation.
---
Quick Reference
Essential Commands
# Start project
ddev start
# Stop project
ddev stop
# Restart services
ddev restart
# SSH into web container
ddev ssh
# Run Drush commands
ddev drush cr
ddev drush status
ddev drush config:status
# Run Composer
ddev composer require drupal/module_name
ddev composer update
# Database operations
ddev import-db --file=backup.sql.gz
ddev export-db --file=backup.sql.gz
ddev snapshot
# View logs
ddev logs
ddev logs -f # Follow mode
# Describe project
ddev describe
# Access URLs
ddev launch # Open site in browserBasic .ddev/config.yaml
name: myproject
type: drupal10
docroot: web
php_version: "8.3"
webserver_type: nginx-fpm
database:
type: mariadb
version: "10.6"
nodejs_version: "20"
# Additional services
additional_services:
- solr
# Custom upload/execution limits
upload_dirs:
- web/sites/default/files
# Performance settings
performance_mode: mutagen # For macOS---
Common Workflows
New Drupal Project
# Create project directory
mkdir myproject && cd myproject
# Initialize DDEV
ddev config --project-type=drupal10 --docroot=web --php-version=8.3
# Install Drupal via Composer
ddev composer create drupal/recommended-project
# Install Drush
ddev composer require drush/drush
# Start DDEV
ddev start
# Install Drupal
ddev drush site:install standard --site-name="My Site" --account-name=admin
# Launch site
ddev launchImport Existing Project
# Clone repository
git clone repo-url myproject && cd myproject
# Start DDEV (reads .ddev/config.yaml)
ddev start
# Install dependencies
ddev composer install
# Import database
ddev import-db --file=path/to/backup.sql.gz
# Import files (if needed)
ddev import-files --source=/path/to/files
# Run updates
ddev drush updb -y
ddev drush cr
# Launch
ddev launchDatabase Sync from Pantheon
# Get latest backup from Pantheon
terminus backup:create site.env --element=db
terminus backup:get site.env --element=db --to=backup.sql.gz
# Import to local
ddev import-db --file=backup.sql.gz
# Run updates
ddev drush updb -y
ddev drush cr
# Sanitize for local (optional)
ddev drush sql-sanitize -yDaily Development Workflow
# Morning: Start project
ddev start
# Pull latest code
git pull origin main
# Update dependencies if needed
ddev composer install
# Clear cache
ddev drush cr
# Work on features...
# Create database snapshot before testing
ddev snapshot --name=before-testing
# Test changes...
# If needed, restore snapshot
ddev snapshot restore --name=before-testing
# Evening: Stop project
ddev stop---
Debugging with Xdebug
# Enable Xdebug
ddev xdebug on
# Run your debugger in IDE (PHPStorm, VSCode)
# Set breakpoints and refresh page
# Disable when done (improves performance)
ddev xdebug off
# Check Xdebug status
ddev xdebug statusVSCode launch.json:
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html": "${workspaceFolder}"
}
}---
Performance Optimization
macOS Performance (Mutagen)
# .ddev/config.yaml
performance_mode: mutagen# Restart after config change
ddev restartNFS Mount (Alternative for macOS)
# .ddev/config.yaml
nfs_mount_enabled: trueDatabase Tuning
# .ddev/config.yaml
database:
type: mariadb
version: "10.6"
# Create .ddev/mysql/my.cnf
[mysqld]
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M---
Custom Commands
Create project-specific commands in .ddev/commands/web/:
Example: .ddev/commands/web/fresh-install
#!/bin/bash
## Description: Fresh Drupal install from scratch
## Usage: fresh-install
## Example: ddev fresh-install
set -e
echo "Installing fresh Drupal site..."
# Drop existing database
drush sql-drop -y
# Install Drupal
drush site:install standard \
--site-name="My Site" \
--account-name=admin \
--account-pass=admin \
-y
# Import config if exists
if [ -d /var/www/html/config/default ]; then
drush config:import -y
fi
# Clear cache
drush cr
echo "Fresh install complete!"
echo "Login: admin / admin"Make it executable:
chmod +x .ddev/commands/web/fresh-install
ddev fresh-install---
Best Practices
1. Commit .ddev/config.yaml - Share config with team 2. Use ddev composer instead of local composer 3. Don't commit database snapshots - Too large 4. Create snapshots before risky operations 5. Disable Xdebug when not debugging - Performance impact 6. Use mutagen on macOS - Much faster file sync 7. Regular ddev poweroff - Free up system resources 8. Version pin services - PHP, database, Node.js 9. Use hooks for automation - Post-start tasks 10. Document custom commands - Help team members
---
Common Issues
Site Not Loading
# Restart project
ddev restart
# Check status
ddev describe
# View logs
ddev logs
# Clear Drupal cache
ddev drush crDatabase Connection Error
# Check database is running
ddev describe
# Verify settings.php or settings.ddev.php exists
ddev ssh
ls web/sites/default/settings*.phpPort Conflicts
# Stop all DDEV projects
ddev poweroff
# Check for port conflicts
lsof -i :80 -i :443
# Change router HTTP port if needed
ddev config --router-http-port=8080 --router-https-port=8443Slow Performance on macOS
# Enable mutagen
ddev config --performance-mode=mutagen
ddev restart
# Or use NFS
ddev config --nfs-mount-enabled=true
ddev restartPHP Deprecation Warnings in Drush
If you're seeing PHP deprecation warnings when running Drush commands (especially with PHP 8.4), create a custom PHP configuration file to suppress them:
`.ddev/php/drush.ini`:
; Suppress PHP deprecation warnings for Drush commands
[PHP]
error_reporting = 22527
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /tmp/php-errors.logThen restart DDEV:
ddev restartHow it works:
error_reporting = 22527equalsE_ALL & ~E_DEPRECATEDdisplay_errors = Offprevents warnings from appearing on STDERRdisplay_startup_errors = Offsuppresses bootstrap warnings- Errors are logged to
/tmp/php-errors.loginstead of being displayed
This configuration applies to both web and CLI contexts since DDEV copies .ddev/php/*.ini files to both /etc/php/[version]/cli/conf.d/ and /etc/php/[version]/fpm/conf.d/.
Docker Desktop overlay2 I/O Errors
If Docker Desktop gets into a bad state producing overlay2 or containerd I/O errors such as:
Error response from daemon: error creating temporary lease: write /var/lib/desktop-containerd/daemon/io.containerd.metadata.v1.bolt/meta.db: input/output errorError response from daemon: open /var/lib/docker/overlay2/...: input/output errorA normal quit and restart of Docker Desktop is not sufficient. You must force quit ALL Docker processes (via Activity Monitor or killall -9 Docker / killall -9 com.docker.hyperkit), then relaunch Docker Desktop. Only a full force quit clears the corrupted state.
Unhealthy Containers / Mutagen Sync Hanging
After Docker crashes or force-quits, DDEV can get into a bad state where:
ddev starthangs at "Starting Mutagen sync process..."- Web container reports unhealthy (
phpstatus:FAILED,mailpit:FAILED) ddev mutagen resetfails with "CreateOrResumeMutagenSync Failure"
Fix (run in order):
# 1. Full power off to clean up all containers and networks
ddev poweroff
# 2. Start fresh
ddev startIf ddev poweroff doesn't resolve it:
# 1. Stop DDEV
ddev stop
# 2. Reset the Mutagen daemon
~/.ddev/bin/mutagen daemon stop
~/.ddev/bin/mutagen daemon start
# 3. Reset Mutagen sync (removes Docker volume, forces full resync)
ddev mutagen reset
# 4. Start fresh
ddev startMonitoring commands while troubleshooting:
ddev mutagen status -l # Detailed sync status
ddev mutagen monitor # Real-time sync progress
docker inspect --format "{{ json .State.Health }}" ddev-<project>-web # Container health---
Multi-Project Management
# List all projects
ddev list
# Stop all projects
ddev poweroff
# Remove stopped projects
ddev delete <project-name>
# Remove all project containers (keep files)
ddev delete --omit-snapshot --yes <project-name>---
Related Skills
- @drupal-pantheon - Deploy to Pantheon from DDEV
- @drupal-config-mgmt - Config management workflows
- @drupal-contrib-mgmt - Module management with Composer
- @drupal-at-your-fingertips - General Drupal patterns
---
Official Documentation: https://ddev.readthedocs.io Drupal DDEV Quickstart: https://ddev.readthedocs.io/en/stable/users/quickstart/ Community Support: https://discord.gg/5wjP76mBJD
.ddev/config.yaml Reference
Complete reference for DDEV configuration file.
---
Complete Example
name: myproject
type: drupal10
docroot: web
php_version: "8.3"
webserver_type: nginx-fpm
xdebug_enabled: false
additional_hostnames: []
additional_fqdns: []
database:
type: mariadb
version: "10.6"
nodejs_version: "20"
composer_version: "2"
router_http_port: "80"
router_https_port: "443"
web_environment:
- ENVIRONMENT=dev
- CUSTOM_VAR=value
upload_dirs:
- web/sites/default/files
performance_mode: mutagen # macOS only
nfs_mount_enabled: false
use_dns_when_possible: true
timezone: America/New_York
omit_containers: []
additional_services: []
override_config: false
provider: default
hooks:
post-start:
- exec: drush cr---
Core Settings
Project Name
name: myproject- Must be unique across all DDEV projects on machine
- Used for container names and URLs
- Convention: match directory name
Project Type
type: drupal10 # Or drupal9, drupal11Available Drupal types:
drupal10- Drupal 10 (recommended)drupal9- Drupal 9drupal- Auto-detect Drupal versiondrupal11- Drupal 11 (when available)
Document Root
docroot: web # Or docroot, public_html, or ""- Relative to project root
- Empty string
""means project root - Standard:
webfor modern Drupal
---
PHP Configuration
PHP Version
php_version: "8.3"Drupal requirements:
- Drupal 10: PHP 8.1, 8.2, or 8.3
- Drupal 9: PHP 7.4, 8.0, 8.1, or 8.2
- Drupal 11: PHP 8.3+
Web Server
webserver_type: nginx-fpm # Or apache-fpmOptions:
nginx-fpm(recommended, faster)apache-fpm(for .htaccess compatibility)
---
Database Configuration
database:
type: mariadb # Or mysql, postgres
version: "10.6" # MariaDB: 5.5, 10.4, 10.6, 10.11MariaDB versions:
10.6- Recommended for Drupal 10/1110.4- Legacy, still supported10.11- Latest
PostgreSQL (less common for Drupal):
database:
type: postgres
version: "14"---
Node.js
nodejs_version: "20" # 18, 20, 21, etc.Used for:
- Theme compilation (Gulp, Webpack)
- Frontend tooling
- Build processes
---
Performance Settings
Mutagen (macOS Performance Boost)
performance_mode: mutagenBenefits:
- 5-10x faster file operations on macOS
- Two-way sync between host and container
- Dramatically improves page load times
Trade-off:
- Small delay in file sync (~100ms)
- Slight memory overhead
NFS Mount (Alternative)
nfs_mount_enabled: trueWhen to use:
- macOS performance issues
- Alternative to mutagen
- Requires NFS server setup
---
Xdebug
xdebug_enabled: false # Change to true to enable by defaultBetter approach: Enable on demand
ddev xdebug on # Enable when needed
ddev xdebug off # Disable for better performance---
Additional Hostnames
additional_hostnames:
- api
- adminCreates:
api.myproject.ddev.siteadmin.myproject.ddev.site
Use case: Multi-domain or subdomain testing
---
Additional FQDNs
additional_fqdns:
- example.local
- test.example.comFully-qualified domain names for testing specific domains.
Requires hosts file entry:
127.0.0.1 example.local test.example.com---
Upload Directories
upload_dirs:
- web/sites/default/files
- web/sites/default/privateDirectories synced with ddev import-files
---
Environment Variables
web_environment:
- ENVIRONMENT=dev
- DRUPAL_ENV=local
- CUSTOM_KEY=valueAvailable in PHP as $_ENV['ENVIRONMENT']
---
Composer Version
composer_version: "2" # Or "1", """2"- Composer 2 (recommended)"1"- Composer 1 (legacy)""- Latest stable
---
Custom Ports
router_http_port: "80"
router_https_port: "443"Change if port conflicts:
router_http_port: "8080"
router_https_port: "8443"Access via: http://myproject.ddev.site:8080
---
Additional Services
additional_services:
- solr
- elasticsearch
- redis
- memcachedSolr Example
additional_services:
- solr:8
# Create .ddev/docker-compose.solr.yaml if neededRedis Example
additional_services:
- redis---
Hooks
Automate tasks at specific points:
hooks:
# After ddev start
post-start:
- exec: composer install
- exec: drush cr
- exec-host: echo "Project started"
# Before ddev start
pre-start:
- exec: echo "Starting project..."
# After composer
post-composer:
- exec: drush cr
# After database import
post-import-db:
- exec: drush updb -y
- exec: drush cr
- exec: drush user:password admin "admin"Hook types:
pre-start,post-startpre-stop,post-stoppre-import-db,post-import-dbpre-composer,post-composerpre-snapshot,post-snapshot
Exec types:
exec- Run in web containerexec-host- Run on host machine
---
Omit Containers
omit_containers:
- ddev-ssh-agent
- dbaSkip containers you don't need to save resources.
---
Timezone
timezone: America/New_YorkSets container timezone (affects logs, cron, etc.)
---
Override Config
override_config: truePrevent ddev config from modifying config.yaml.
Use case: When config.yaml is managed by team/CI
---
Provider Integration
provider: pantheon # Or platform, defaultPantheon:
provider: pantheon
web_environment:
- PANTHEON_ENVIRONMENT=devPlatform.sh:
provider: platform---
Full Drupal 10 Example
name: mysite
type: drupal10
docroot: web
php_version: "8.3"
webserver_type: nginx-fpm
xdebug_enabled: false
database:
type: mariadb
version: "10.6"
nodejs_version: "20"
composer_version: "2"
# Performance (macOS)
performance_mode: mutagen
# Additional services
additional_services:
- solr:8
# Upload directories
upload_dirs:
- web/sites/default/files
# Environment variables
web_environment:
- ENVIRONMENT=local
- DRUPAL_ENV=dev
# Hooks
hooks:
post-start:
- exec: composer install
- exec: drush cr
post-import-db:
- exec: drush updb -y
- exec: drush cr
- exec: drush user:password admin "admin"
- exec: drush sql-sanitize -y
# Timezone
timezone: America/New_York---
Best Practices
1. Commit to repository - Share with team 2. Pin versions explicitly - PHP, database, Node 3. Use mutagen on macOS - Huge performance boost 4. Disable Xdebug by default - Enable only when needed 5. Automate with hooks - Post-import, post-start tasks 6. Document custom settings - Add comments in YAML 7. Match production - Same PHP/DB versions as live
---
Migration Examples
From Lando
# Lando .lando.yml
name: mysite
recipe: drupal10
config:
php: '8.2'
webroot: web
# DDEV equivalent
name: mysite
type: drupal10
docroot: web
php_version: "8.2"From MAMP/XAMPP
# DDEV config for existing MAMP site
name: mysite
type: drupal10
docroot: . # Often no subdirectory
php_version: "8.1"
database:
type: mysql # If was using MySQL not MariaDB
version: "8.0"---
Last updated: 2024-11-05 Official docs: https://ddev.readthedocs.io/en/stable/users/configuration/config/
DDEV Database Operations
Complete guide for database import, export, and management workflows in DDEV.
Table of Contents
---
Import Methods
Method 1: DDEV Import Command (Recommended for Most Cases)
# Import compressed backup
ddev import-db --file=backup.sql.gz
# Import uncompressed SQL file
ddev import-db --file=backup.sql
# Import from URL
ddev import-db --src=https://example.com/backup.sql.gzPros:
- Automatic decompression (.sql.gz, .sql.zip, .tar.gz)
- Progress indicator
- Error handling
- Official DDEV command
Cons:
- Slightly slower than direct methods
- May have issues with very large files
Method 2: Direct MySQL Import
# Simple and fast
ddev mysql < backup.sql
# With pipe from cat
cat backup.sql | ddev mysql
# Compressed file (decompress first)
gunzip -c backup.sql.gz | ddev mysqlPros:
- Fast
- Simple
- Works with pipes
- Good for automation
Cons:
- No progress indicator
- Manual decompression needed
- Less error handling
Method 3: Drush SQL Query
# Using Drush
ddev drush sql:query --file=backup.sql
# Short version
ddev drush sqlq --file=backup.sqlPros:
- Drupal-native approach
- Good for Drush-based workflows
- Can be used in custom scripts
Cons:
- Requires Drush
- Slower than direct MySQL
Method 4: Manual Import (Project-Specific Pattern)
Use this when you need precise control over the import process using Drush connection parameters.
# 1. Place SQL file in docroot so container can access it
cp /path/to/backup.sql docroot/backup.sql
# 2. Import using drush sql:connect
ddev exec "$(drush sql:connect) < docroot/backup.sql"
# 3. Clean up
rm docroot/backup.sqlWhy this works:
- SQL file is placed in
docroot/which is mounted in the container $(drush sql:connect)expands to the mysql connection command with all parametersddev execruns the command inside the container where it can access the file- The
<redirect happens inside the container context
When to use:
- You need to use Drush's database connection settings
- Working with project-specific database configurations
- Debugging connection issues
- Custom backup workflows
Common variations:
# Import and run specific queries after
ddev exec "$(drush sql:connect) < docroot/backup.sql && drush sqlq 'UPDATE system SET status=1'"
# Import with verbose output
ddev exec "bash -c '$(drush sql:connect) < docroot/backup.sql'"---
Export Methods
DDEV Export Command
# Export to compressed file
ddev export-db --file=backup.sql.gz
# Export uncompressed
ddev export-db --file=backup.sql
# Export with gzip compression
ddev export-db --gzip=false --file=backup.sqlDirect MySQL Export
# Export using mysqldump
ddev mysqldump > backup.sql
# Export compressed
ddev mysqldump | gzip > backup.sql.gz
# Export with Drush
ddev drush sql:dump --result-file=../backup.sql
ddev drush sql:dump --gzip --result-file=../backup.sql.gzExport Specific Tables
# Export only specific tables
ddev mysqldump database_name table1 table2 > backup.sql
# Export structure only (no data)
ddev mysqldump --no-data > structure.sql
# Export data only (no structure)
ddev mysqldump --no-create-info > data.sql---
Snapshots
Database snapshots are quick backups you can restore later.
Create Snapshot
# Create named snapshot
ddev snapshot --name=before-testing
# Create snapshot with auto-generated name
ddev snapshotList Snapshots
# List all snapshots for current project
ddev snapshot --list
# Show with details
ddev snapshot --list --allRestore Snapshot
# Restore specific snapshot
ddev snapshot restore --name=before-testing
# Restore latest snapshot
ddev snapshot restore --latestDelete Snapshots
# Delete specific snapshot
ddev snapshot --cleanup --name=before-testing
# Delete all snapshots for current project
ddev snapshot --cleanup --allSnapshot Workflow Example
# Before making risky changes
ddev snapshot --name=before-module-update
# Make changes, test...
ddev composer require drupal/some_module
ddev drush updb -y
# If something breaks, restore
ddev snapshot restore --name=before-module-update
# If everything works, clean up old snapshot
ddev snapshot --cleanup --name=before-module-update---
Database Access
MySQL CLI
# Open MySQL CLI
ddev mysql
# Run SQL query from command line
ddev mysql -e "SELECT COUNT(*) FROM users"
# Use Drush
ddev drush sqlcMySQL Connection Details
# Get connection string
ddev drush sql:connect
# Example output:
# mysql --database=db --host=db --user=db --password=db
# Get connection info as JSON
ddev describeAccess from Host Machine
# Get connection details
ddev describe
# Connect from host using displayed port
mysql -h 127.0.0.1 -P 32768 -u db -pdb db---
Sanitization
Clean sensitive data for local development.
Using Drush SQL Sanitize
# Sanitize all user emails and passwords
ddev drush sql:sanitize -y
# Custom sanitization
ddev drush sqlq "UPDATE users_field_data SET mail = CONCAT('user', uid, '@example.com') WHERE uid > 0"
# Reset all user passwords
ddev drush sqlq "UPDATE users_field_data SET pass = '\$S\$D7...' WHERE uid > 0"Sanitization Script Example
Create .ddev/commands/web/sanitize-db:
#!/bin/bash
## Description: Sanitize database for local development
## Usage: sanitize-db
## Example: ddev sanitize-db
set -e
echo "Sanitizing database..."
# Sanitize emails
drush sqlq "UPDATE users_field_data SET mail = CONCAT('user', uid, '@localhost.local') WHERE uid > 0"
# Reset all user passwords to 'admin'
drush sqlq "UPDATE users_field_data SET pass = '\$S\$D7p6QjHXHq6Qw6.N5Q5Q5Q5Q5Q5Q5Q5Q5Q' WHERE uid > 0"
# Clear sessions
drush sqlq "TRUNCATE sessions"
# Clear cache
drush cr
echo "Sanitization complete!"
echo "All user passwords reset to: admin"Make executable:
chmod +x .ddev/commands/web/sanitize-db
ddev sanitize-db---
Troubleshooting
Import Fails with "Access Denied"
# Check database credentials
ddev describe
# Verify settings.php or settings.ddev.php exists
ddev ssh
cat web/sites/default/settings.ddev.php
# Restart database
ddev restartImport is Very Slow
# Use direct MySQL import instead of ddev import-db
gunzip -c backup.sql.gz | ddev mysql
# Or use pv for progress
pv backup.sql.gz | gunzip | ddev mysql
# Increase database resources in .ddev/mysql/my.cnf
[mysqld]
innodb_buffer_pool_size = 1G
max_allowed_packet = 512M"Table doesn't exist" after import
# Verify import completed successfully
ddev mysql -e "SHOW TABLES"
# Check for errors during import
ddev logs | grep -i error
# Try re-importing
ddev mysql -e "DROP DATABASE db; CREATE DATABASE db"
ddev import-db --file=backup.sql.gzDatabase is too large
# Import only structure first
ddev mysqldump --no-data db > structure.sql
ddev mysql < structure.sql
# Then import data for specific tables
ddev mysqldump db important_table1 important_table2 > critical_data.sql
ddev mysql < critical_data.sql
# Skip large cache/log tables
ddev import-db --file=backup.sql.gz
ddev mysql -e "TRUNCATE cache_bootstrap"
ddev mysql -e "TRUNCATE cache_render"
ddev mysql -e "TRUNCATE watchdog""Commands out of sync" error
# Usually caused by multiple queries in single transaction
# Split your SQL file or import in smaller chunks
# Or disable multi-query
ddev mysql --init-command="SET SESSION sql_mode=''" < backup.sql---
Best Practices
1. Use snapshots before risky operations - Quick rollback if needed 2. Compress large backups - Save disk space and transfer time 3. Sanitize production data - Never work with real user emails/passwords locally 4. Regular backups - Before major updates or deployments 5. Clean up old snapshots - They consume disk space 6. Test imports - Verify data integrity after import 7. Document custom workflows - Create project-specific commands 8. Use .gitignore - Never commit database dumps to git
---
Related References
- Drush Commands - Drush database commands
- Config YAML - Database configuration
- Custom Commands - Create custom database scripts
Official DDEV Documentation:
- https://ddev.readthedocs.io/en/stable/users/usage/database-management/
- https://ddev.readthedocs.io/en/stable/users/usage/snapshots/
Local Solr with DDEV
Overview
DDEV can run a local Solr service for Search API development and testing. This is useful for testing search functionality without relying on Pantheon's Solr service.
Installation
Add Solr Service
ddev get ddev/ddev-solr
ddev restartThis creates:
.ddev/docker-compose.solr.yaml- Solr service configuration.ddev/solr/- Solr configuration directory.ddev/commands/host/solr-admin- Command to open Solr admin UI
Configuration Files Created
.ddev/
├── docker-compose.solr.yaml # Solr service definition
├── solr/
│ ├── configsets/ # Solr core configurations
│ ├── lib/ # Custom Solr libraries
│ └── security.json # Basic auth config
└── commands/
├── host/solr-admin # Open Solr admin UI
└── solr/ # Solr commands inside containerAccess Points
| Service | URL | Credentials |
|---|---|---|
| Solr Admin UI | https://sitename.ddev.site:8943/solr | solr / SolrRocks |
| Internal (from Drupal) | http://solr:8983/solr | solr / SolrRocks |
Note: External port is 8943 (HTTPS), internal port is 8983 (HTTP).
Drupal Configuration
Override Search API Server in settings.ddev.php
CRITICAL: Use settings.ddev.php for DDEV-specific overrides, NOT settings.local.php.
// Override Pantheon Solr server to use local DDEV Solr
$config['search_api.server.pantheon_search']['backend_config']['connector'] = 'standard';
$config['search_api.server.pantheon_search']['backend_config']['connector_config'] = [
'scheme' => 'http',
'host' => 'solr', // DDEV container hostname
'port' => '8983', // Internal port
'path' => '/',
'core' => 'drupal', // Core name from configset
'timeout' => 5,
'index_timeout' => 5,
'optimize_timeout' => 10,
'finalize_timeout' => 30,
'solr_version' => '9',
'http_method' => 'AUTO',
'commit_within' => 1000,
'username' => 'solr',
'password' => 'SolrRocks',
];Why settings.ddev.php?
✅ DO: Put DDEV overrides in settings.ddev.php
- Only loads when
IS_DDEV_PROJECT=true - Never loads on Pantheon
- DDEV-managed file
❌ DON'T: Put DDEV overrides in settings.local.php
- May be tracked in git
- May be deployed to production
- Can break production if it contains DDEV-specific config
Common Workflows
Verify Solr is Running
# Check service status
ddev describe
# Should show:
# solr OK https://sitename.ddev.site:8943Index Content
# Clear and reindex
ddev drush search-api:clear
ddev drush search-api:index
# Check status
ddev drush search-api:statusTest Search Queries
# Search via Drush
ddev drush search-api:search mixed_entities "guitar"
# Or via Solr admin UI
ddev solr-adminView Solr Logs
# Recent logs
ddev logs -s solr | tail -50
# Follow logs
ddev logs -s solr -fAccess Solr Admin UI
# Open in browser
ddev solr-admin
# Or manually visit
# https://sitename.ddev.site:8943/solr
# Username: solr
# Password: SolrRocksTroubleshooting
Connection Refused
Problem: Drupal can't connect to Solr
Solution:
# Verify Solr is running
ddev describe | grep solr
# Restart if needed
ddev restart
# Check Drupal can reach Solr
ddev exec curl http://solr:8983/solr/admin/pingWrong Connector on Pantheon
Problem: Production shows Could not resolve host: solr
Cause: DDEV Solr config deployed to Pantheon
Solution:
- Move config from
settings.local.php→settings.ddev.php - Ensure
settings.local.phpis in.gitignore - Never commit DDEV-specific settings to git if they override production services
Core Not Found
Problem: Solr core 'drupal' doesn't exist
Solution:
# Check cores
ddev solr-admin
# Navigate to Core Admin
# If missing, recreate configset
ddev restartIndexing Fails
Problem: Search API indexing times out or fails
Solution:
# Check Solr logs for errors
ddev logs -s solr
# Verify core is healthy
curl -u solr:SolrRocks "https://sitename.ddev.site:8943/solr/drupal/admin/ping"
# Clear and retry
ddev drush search-api:clear
ddev drush search-api:indexConfiguration Best Practices
1. Settings File Hierarchy
// settings.php - Shared settings
include 'settings.pantheon.php'; // Pantheon auto-config
// settings.ddev.php - DDEV-only (only loads if IS_DDEV_PROJECT=true)
$config['search_api.server.pantheon_search']['backend_config']['connector'] = 'standard';
// settings.local.php - Developer-specific (gitignored)
// Use for personal overrides only, never DDEV service config2. Connector Selection
| Environment | Connector | Config |
|---|---|---|
| Pantheon (dev/test/live) | pantheon | Auto-configured by Pantheon |
| DDEV Local | standard | Override in settings.ddev.php |
| Other Local | standard or pantheon | Override in settings.local.php |
3. .gitignore
Ensure these are ignored:
docroot/sites/default/settings.local.php
docroot/sites/default/settings.ddev.php # DDEV manages thisIntegration with Pantheon
Syncing Data
# Pull database from Pantheon
terminus backup:create sitename.dev --element=db
terminus backup:get sitename.dev --element=db --to=backup.sql.gz
# Import locally
ddev import-db --file=backup.sql.gz
# Reindex with local Solr
ddev drush search-api:clear
ddev drush search-api:indexTesting Before Deploy
# Test search functionality locally
ddev drush search-api:status
# Run searches
ddev drush search-api:search mixed_entities "test query"
# Verify via UI
ddev launch /searchDeploy Checklist
- [ ] Verify no DDEV Solr config in
settings.local.php - [ ] Ensure
settings.local.phpis in.gitignore - [ ] DDEV overrides only in
settings.ddev.php - [ ] Test that Pantheon connector works after config import
- [ ] Clear cache after deployment
Advanced: Custom Solr Configuration
Add Custom Configset
# Create custom configset
mkdir -p .ddev/solr/configsets/mycore
# Copy base config
cp -r .ddev/solr/configsets/drupal/* .ddev/solr/configsets/mycore/
# Edit schema
vim .ddev/solr/configsets/mycore/conf/managed-schema.xml
# Restart to apply
ddev restartUse Different Solr Version
# .ddev/docker-compose.solr.yaml
services:
solr:
image: solr:8 # Change versionRelated Documentation
Lessons Learned
Settings File Isolation Incident
What Happened:
- Added Solr config to
settings.local.php - File was tracked in git and deployed to Pantheon
- Overwrote Pantheon's Solr connector with DDEV hostname
solr:8983 - Production search failed with "Could not resolve host: solr"
Root Cause:
// settings.local.php (WRONG - deployed to production)
$config['search_api.server.pantheon_search']['backend_config']['connector'] = 'standard';
$config['search_api.server.pantheon_search']['backend_config']['connector_config']['host'] = 'solr';Fix: 1. Moved config to settings.ddev.php (only loads in DDEV) 2. Removed settings.local.php from git 3. Added settings.local.php to .gitignore
Prevention:
- ✅ Always use
settings.ddev.phpfor DDEV service overrides - ✅ Keep
settings.local.phpgitignored and developer-specific - ✅ Test configuration sync:
ddev drush config:status - ✅ Verify settings load order in
settings.php
Key Principle
DDEV settings should ONLY exist in files that are environment-aware:
// settings.php - Check before loading
if (getenv('IS_DDEV_PROJECT') == 'true' && is_readable($ddev_settings)) {
require $ddev_settings; // ✅ Only loads in DDEV
}
if (!isset($_ENV['PANTHEON_ENVIRONMENT']) && file_exists($local_settings)) {
include $local_settings; // ✅ Only loads outside Pantheon
}This prevents DDEV configuration from breaking production environments.
Related skills
How it compares
Reference skill for DDEV YAML—not a replacement for Drupal module development or production hosting runbooks.
FAQ
Who is drupal-ddev for?
Developers and small teams shipping Drupal sites who want agent help drafting or reviewing DDEV configuration without reading the entire upstream docs each time.
When should I use drupal-ddev?
When scoping a Validate prototype, wiring Build backend containers, or tuning Operate-local parity (hooks, env, DB version) before debugging deployment mismatches.
Is drupal-ddev safe to install?
Check the Security Audits panel on this Prism page; the skill is documentation-heavy—review any suggested web_environment secrets and never commit production credentials into config.yaml.