DDEV is an open-source local web development environment manager that uses Docker containers to create isolated, reproducible development environments. Written in Go, DDEV supports PHP-based applications (Drupal, WordPress, Laravel, TYPO3, Magento, etc.) and Node.js projects across macOS, Linux, Windows, and WSL2.
DDEV manages the complete lifecycle of local development environments:
| Capability | Description |
|---|---|
| Environment Creation | Runs ddev config to detect project type and generate .ddev/config.yaml |
| Container Orchestration | Starts web server (nginx/Apache + PHP), database (MariaDB/MySQL/PostgreSQL), and optional services |
| HTTP/HTTPS Routing | Provides https://projectname.ddev.site URLs via Traefik router |
| Database Management | Import/export databases, create snapshots, connect external tools |
| Development Tools | Run Composer, npm, Drush, WP-CLI, and other tools inside containers |
| CMS Integration | Auto-configure settings files for Drupal, WordPress, and other frameworks |
DDEV's architecture consists of four layers that transform CLI commands into running Docker containers:
DDEV System Architecture: Four-Layer Model
This architecture enables DDEV to:
appTypeMatrix dispatcherSources: pkg/ddevapp/ddevapp.go82-149 cmd/ddev/cmd/root.go pkg/ddevapp/apptypes.go pkg/ddevapp/app_compose_template.yaml
For detailed information:
The DdevApp struct pkg/ddevapp/ddevapp.go82-149 is the central orchestrator that represents a DDEV project. It contains all configuration and provides lifecycle management methods:
Essential Fields:
Lifecycle Methods:
| Method | Purpose | File Location |
|---|---|---|
NewApp() | Creates DdevApp from config files | pkg/ddevapp/config.go63-176 |
Init() | Validates config and initializes app | pkg/ddevapp/ddevapp.go165-200 |
Start() | Generates compose files, starts containers | pkg/ddevapp/start.go |
Stop() | Stops containers, optional snapshot | pkg/ddevapp/stop.go |
Describe() | Returns project status and URLs | pkg/ddevapp/ddevapp.go215-591 |
WriteConfig() | Saves configuration to disk | pkg/ddevapp/config.go226-320 |
Sources: pkg/ddevapp/ddevapp.go82-149 pkg/ddevapp/config.go63-176 pkg/ddevapp/ddevapp.go165-200
DDEV uses a layered configuration system where later configurations override earlier ones:
Configuration Hierarchy: Merge Order
Configuration Files:
| File | Purpose | Scope | Example Settings |
|---|---|---|---|
| Built-in defaults | Base values | Global | PHP 8.4, nginx-fpm, MariaDB 11.8 |
~/.ddev/global_config.yaml | Machine-wide settings | All projects | Router ports, performance mode |
.ddev/config.yaml | Project settings | Single project | Name, type, docroot, PHP version |
.ddev/config.*.yaml | Overrides | Single project | Local customizations, CI settings |
.ddev/config.local.yaml | Local-only (gitignored) | Single project | Developer-specific settings |
The mergeAdditionalConfigIntoApp() function pkg/ddevapp/config.go1426-1636 handles field-specific merge logic:
override_config: true: Complete replacement instead of mergeSources: pkg/ddevapp/config.go1426-1636 pkg/globalconfig/global_config.go41-79 pkg/nodeps/constants.go
The appTypeMatrix pkg/ddevapp/apptypes.go is a map that provides CMS-specific behavior:
Each CMS type implements the appTypeFuncs interface with methods for:
settings.php, .env, additional.phpsites/default/filesExample: Drupal Implementation
| Function | Purpose | Implementation |
|---|---|---|
createDrupalSettingsFile() | Creates settings.php with DDEV include | pkg/ddevapp/drupal.go |
writeDrupalSettingsDdevPhp() | Injects database credentials | pkg/ddevapp/drupal.go |
drupalPostStartAction() | Sets permissions on files/ | pkg/ddevapp/drupal.go |
getDrupalUploadDirs() | Returns ["sites/default/files"] | pkg/ddevapp/drupal.go |
Sources: pkg/ddevapp/apptypes.go pkg/ddevapp/drupal.go pkg/ddevapp/wordpress.go pkg/ddevapp/laravel.go
DDEV creates several Docker containers for each project:
| Container | Purpose | Image |
|---|---|---|
ddev-{name}-web | Web server + PHP + development tools | ddev/ddev-webserver containers/ddev-webserver/Dockerfile1-266 |
ddev-{name}-db | Database server | ddev/ddev-dbserver containers/ddev-dbserver/Dockerfile1-162 |
ddev-router | Shared Traefik reverse proxy (one per host) | ddev/ddev-traefik-router |
ddev-ssh-agent | SSH key management (optional, one per host) | ddev/ddev-ssh-agent |
The ddev-router container is shared across all projects and routes requests based on hostname to the appropriate project's web container.
Sources: containers/ddev-webserver/Dockerfile1-266 containers/ddev-dbserver/Dockerfile1-162 pkg/ddevapp/router.go
DDEV follows a simple workflow from installation to active development:
User Workflow: From Installation to Development
CLI Command Execution Flow:
When a user runs ddev start, the following execution path occurs:
RootCmd.Execute() cmd/ddev/cmd/root.go routes to StartCmd.Run() cmd/ddev/cmd/start.goapp.Init() pkg/ddevapp/ddevapp.go165-200 loads and validates configurationapp.WriteDockerComposeYAML() renders app_compose_template.yaml pkg/ddevapp/app_compose_template.yaml1-328dockerutil.ComposeUp() pkg/dockerutil/dockerutils.go executes docker compose uppost-start hooks defined in config.yamlSources: cmd/ddev/cmd/root.go cmd/ddev/cmd/start.go pkg/ddevapp/ddevapp.go165-200 pkg/ddevapp/app_compose_template.yaml1-328 pkg/dockerutil/dockerutils.go
DDEV dynamically generates Docker Compose configuration from templates and project settings:
Docker Compose Generation Pipeline
The base Docker Compose file is generated from app_compose_template.yaml pkg/ddevapp/app_compose_template.yaml1-328 embedded in the DDEV binary. This template uses Go template syntax to conditionally include services and volumes based on DdevApp configuration.
Template Variables:
Key variables passed to the template from DockerEnv():
| Variable | Purpose | Example Value |
|---|---|---|
COMPOSE_PROJECT_NAME | Docker Compose project name | ddev-myproject |
DDEV_SITENAME | Project name | myproject |
DDEV_HOSTNAME | Primary hostname | myproject.ddev.site |
DDEV_PHP_VERSION | PHP version | 8.4 |
DDEV_WEBIMAGE | Web container image | ddev/ddev-webserver:20260204_stasadev_avif |
DDEV_DBIMAGE | Database container image | ddev/ddev-dbserver-mariadb-11.8:v1.25.0 |
After generating the base file, DDEV finds additional compose files from:
.ddev/docker-compose.*.yaml (user customizations)These are merged using Docker Compose's native multi-file support (docker compose --file base.yaml --file override.yaml).
Sources: pkg/ddevapp/app_compose_template.yaml1-328 pkg/dockerutil/dockerutils.go pkg/ddevapp/compose_template.go
DDEV supports multiple Docker providers across different operating systems:
Supported Platforms:
| Platform | Docker Providers | Performance Mode Default |
|---|---|---|
| macOS | Docker Desktop, OrbStack, Colima, Lima, Rancher Desktop | Mutagen |
| Linux | Docker CE, Podman | Native bind mounts |
| Windows | Docker Desktop, Rancher Desktop | Mutagen |
| WSL2 | Docker Desktop, Docker CE (inside WSL2) | Native bind mounts (inside WSL2) or Mutagen |
Provider Detection:
DDEV detects the active Docker provider using functions in pkg/dockerutil/dockerutils.go:
The GetDockerPlatform() function pkg/version/version.go58-93 returns a string describing the platform:
docker-desktopcolimaorbstacklimarancher-desktopwsl2-docker-celinux-dockerpodmanFile Synchronization Strategies:
Different platforms use different file synchronization approaches for performance:
| Platform | Strategy | Implementation | Trade-offs |
|---|---|---|---|
| Linux | Native bind mounts | Direct Docker volume mount | Fast, no overhead, POSIX-compliant |
| macOS (default) | Mutagen | Two-way sync to Docker volume | Faster than bind mounts, requires sync |
| Windows (default) | Mutagen | Two-way sync to Docker volume | Much faster than bind mounts |
| WSL2 (projects in WSL2) | Native bind mounts | Direct mount | Fast, Linux filesystem |
| WSL2 (projects in /mnt/c) | Mutagen | Two-way sync | Faster than cross-filesystem mount |
The IsMutagenEnabled() method checks if Mutagen is active for the current project. Mutagen synchronization is managed by MutagenSyncFlush() pkg/ddevapp/mutagen.go and related functions.
Sources: pkg/version/version.go58-93 pkg/dockerutil/dockerutils.go pkg/ddevapp/mutagen.go pkg/nodeps/utils.go
DDEV dynamically generates Docker Compose configuration from Go templates and project settings:
Docker Compose Generation and Execution Flow
Key Functions:
| Function | Location | Purpose |
|---|---|---|
DockerEnv() | Part of DdevApp | Builds environment variable map for template rendering |
WriteDockerComposeYAML() | Part of DdevApp | Renders app_compose_template.yaml to .ddev-docker-compose-base.yaml |
ComposeCmd() | pkg/dockerutil/dockerutils.go | Constructs docker compose command with all --file flags |
ComposeUp() | pkg/dockerutil/dockerutils.go | Executes docker compose up with health checks |
ComposeDown() | pkg/dockerutil/dockerutils.go | Stops and optionally removes containers and volumes |
Template Rendering:
The base Docker Compose file is generated from app_compose_template.yaml embedded in the binary. This template uses Go template syntax to conditionally include services:
The template is rendered with data from DdevApp struct, producing the base compose file.
Service Discovery and Merging:
After generating the base file, DDEV finds additional compose files pkg/ddevapp/compose_template.go:
docker-compose.*.yaml files.ddev/These are merged using Docker Compose's native file merging (multiple --file flags).
Sources: pkg/ddevapp/compose_template.go pkg/dockerutil/dockerutils.go pkg/ddevapp/ddevapp.go
The ddev-router container runs Traefik and shares ports 80/443 across all projects:
StartRouterIfNecessary() in pkg/ddevapp/router.go60-200 aggregates port requirementsVIRTUAL_HOST Docker labelsPlatform-specific strategies for host-to-container file access:
| Platform | Default Strategy | Implementation |
|---|---|---|
| Linux | Native bind mounts | Direct Docker volume mount |
| macOS | Mutagen | MutagenSyncFlush() in pkg/ddevapp/mutagen.go |
| Windows | Mutagen | MutagenSyncFlush() in pkg/ddevapp/mutagen.go |
Mutagen creates Docker volumes and synchronizes bidirectionally between host and container. The IsMutagenEnabled() method checks if Mutagen is active.
Database operations are encapsulated in methods like:
ImportDB(): Imports SQL dumps with compression detection pkg/ddevapp/ddevapp.go876-1052ExportDB(): Exports database to SQL fileSnapshot(): Creates database snapshots in .ddev/db_snapshots/Sources: pkg/ddevapp/ddevapp.go876-1052 pkg/ddevapp/router.go pkg/ddevapp/mutagen.go
DDEV supports macOS, Linux, and Windows with multiple Docker providers:
Platform Detection Functions:
| Function | Location | Returns True When |
|---|---|---|
IsDockerDesktop() | pkg/dockerutil/dockerutils.go | Docker Desktop is running |
IsColima() | pkg/dockerutil/dockerutils.go | Colima is the active Docker context |
IsOrbStack() | pkg/dockerutil/dockerutils.go | OrbStack is running (macOS) |
IsLima() | pkg/dockerutil/dockerutils.go | Lima context is active |
IsRancherDesktop() | pkg/dockerutil/dockerutils.go | Rancher Desktop detected |
IsRootless() | pkg/dockerutil/dockerutils.go | Docker is running in rootless mode |
IsPodman() | pkg/dockerutil/dockerutils.go | Podman is being used |
Version Information:
The GetDockerPlatform() function pkg/version/version.go58-93 determines the active platform:
Performance Mode Selection:
Different platforms use different file synchronization strategies:
| Platform | Default Performance Mode | Implementation |
|---|---|---|
| Linux | none (native bind mounts) | Direct Docker volume mount, no overhead |
| macOS | mutagen | Mutagen daemon synchronizes to Docker volume pkg/ddevapp/mutagen.go |
| Windows | mutagen | Mutagen daemon synchronizes to Docker volume pkg/ddevapp/mutagen.go |
| WSL2 | none or mutagen | Depends on project location (inside/outside WSL2 filesystem) |
The IsMutagenEnabled() method checks if Mutagen is active for the current project.
Sources: pkg/version/version.go58-93 pkg/dockerutil/dockerutils.go pkg/ddevapp/mutagen.go pkg/nodeps/utils.go
DDEV provides multiple extension mechanisms:
ddev add-on get that add services or configuration.ddev/commands/ directoriesconfig.yamldocker-compose.*.yaml files.ddev/web-build/ or .ddev/db-build/The add-on system is detailed in page Add-on System, custom commands in Custom Commands and Hooks, and customization in Extending and Customizing Environments
Sources: cmd/ddev/cmd/addon.go pkg/ddevapp/ddevapp.go82-149 docs/content/users/extend/customization-extendibility.md
Version constants are defined in pkg/versionconstants/versionconstants.go10-60:
DdevVersion: Set via ldflags during buildWebTag, BaseDBTag: Container image tagsRequiredMutagenVersion: Required Mutagen versionThe build system uses Make for cross-compilation across six platforms (linux/darwin/windows on amd64/arm64). See Build Infrastructure for details.
Sources: pkg/versionconstants/versionconstants.go10-60 pkg/version/version.go22-55 Makefile
Refresh this wiki
This wiki was recently refreshed. Please wait 6 days to refresh again.