Get the best out of DDEV

I’m a Windows guy. I’ve always was, I’ll (probably?) always be. But I’m also a developer that writes quite a lot of PHP, so sometimes things aren’t working as smooth as I would like.

Until recently, my dev environment was a windows nginx.exe with a bunch of php.exe wired up and everything used to work just great. Except when it didn’t: one stupidly huge project was stupidly slow. Like >20s page load slow with xdebug disabled! So I started looking into how I could improve the performance on my machine.

My first attempt: run everything in WSL. But installing the whole stack in WSL wasn’t quite worked as I expected. There was always something off: the debugger wasn’t working. Different projects needs different PHP versions. You know.. Stuff.

My second attempt: create a docker-compose thingy and make things play nice. Although I’ve managed to create a reasonable functional setup, things were still not as smooth as I would like: the debugger was still a hit-or-miss functionality. NodeJS acted out. I wasn’t able to run multiple projects at once. But at least I got multiple PHP versions!

I had other several attempts on using various tools that create a dev environment and at some point I found wp-env, which kind of worked but not really. And then, in one of wp-env github issues, I found a comment: „I use DDEV”.

Hmm, what the hell is this DDEV?!

In short, it’s a tool that helps you to create a local dev environment in about 20s. And it’s packed with goodies. PACKED.

I won’t go on with how you can install – the documentation is amazing; so are the people in the Discord server! – it but I’ll give you some stuff that I’m doing in order to get the best out of it.

Aliases & Functions

One very annoying thing is that all commands needs to be prefixed with ddev. You want to run npm? You have to type ddev npm. Want to run composer? You guess it, you’ll have to type ddev composer. And so on. This soon become a very annoying thingy, so here is how you can fix that.

You must dump all the functions in your ~/.bashrc file. After this, don’t forget to either close & open your terminal or reload your configs

$ . ~/.bashrc 
alias reddev="ddev restart"
alias ddlogs="ddev logs -f"

By default, ddev ssh will SSH into root path. This function will put you on the current path.

  currentRoot=$(ddev describe -j | jq -r '.raw.approot')
  rel=$(echo $currentDir | sed -r "s|^($currentRoot)|/var/www/html|g" ) 
  ddev ssh -d "$rel"

Alternatively, you could create a custom host ddev command, similar to context-composer below

DDEV doesn’t have a quick way to toggle xdebug, you’ll have to either type ddev xdebug to turn it on or ddev xdebug off to turn it… well, off. Here is a nice function that will toggle it instead:

  xdebugStatus=$(ddev xdebug status)                                                                                                                                                                                                                                                                                                                                 
  if [[ $xdebugStatus =~ 'disabled' ]];                                                                                                                                                                                                                                                                                                                              
      ddev xdebug on
      ddev xdebug off
  fi                                                                                                                                                                                                                                                                                                                                                                 } 

Same goes for other commands:

wp(){ ddev wp "$@"; }
npm(){ ddev npm "$@"; }
composer(){ ddev context-composer "$@"; }

⚠️ Obviously, these commands will overwrite your machine instances of wp, npm or composer, so it’s totally up to you if/how you’re going to use them.

Oh, but what’s up with that context-composer thingy? That’s not built in into DDEV, isn’t it? Well, allow me to introduce….

Custom DDEV commands

There are a couple of options on where to store commands:

  • In the project folder (so /your/project/path/.ddev/): do this when the commands you’re writing are tied to that project OR you want to share them with your team members
  • In the global DDEV config (~/.ddev): to this when you want to have custom commands on your machine only.

For brevity, I’ll just use .ddev as path, you decide where to actually put them.

Anyway, commands are defined inside .ddev/commands and they are organized by where they are running. Kind of obvious, right?

Host Commands

These are running on your … well, host. So if your machine is Linux, these commands will run on your Linux. If your machine is Windows/WSL, then the commands will run on Windows/WSL. Easy, right?


On all projects I kind of follow a similar pattern: open a tmux session, create a few panes, open logs in one, maybe run a npm script in another one and so on. Why not create a command for that?

Create a file: .ddev/commands/hosts/tmux and add the following:


## Description: Start tmux session with ddev logs
## Usage: tmux [version]
## Example: "ddev tmux"

tmux new -A -s "$( ddev describe -j | jq -r '' | sed -e 's|[^a-z0-9]|___|ig' )" ;
  split-window -h ; 
  split-window -v ; 
  send-keys 'ddev logs -f' C-m ; 

Quickly switch PHP version

On a recent project I have to migrate from an older PHP version to a newer one. I do this on a separate Git branch, therefore there are days when I have to switch between versions several times. So I need a way of set the php version!

⚠️Note that none of the following will check the availability of the selected version, so you can end up with invalid versions set! If that’s the case, edit the config file and set it manually!

Create a file: .ddev/commands/hosts/php-version


## Description: Display or set the PHP version
## Usage: php-version [version]
## Example: "ddev php-version"

currentVersion=$(ddev describe -j | jq -r '.raw.php_version')

if [ -z "$1" ]; then
  echo "Current PHP version is $currentVersion"
  if [ "$currentVersion" = "$1" ]; then
    echo "PHP version is already set to $1"
    ddev config --php-version "$1"
    ddev restart
    echo "PHP version set to $1"

Alternatively, if you want to set the PHP version only on your machine, while you’re keeping other team members out of this, you can make use of local ddev config files:


## Description: Display or set the PHP version locally
## Usage: php-version-local [version]
## Example: "ddev php-version-local"

currentVersion=$(ddev describe -j | jq -r '.raw.php_version')

if [ ! -f $configFilePath ]; then
  touch $configFilePath

if [ "$currentVersion" = "$1" ]; then 
  echo "PHP version is already set to $1"

if grep -q "php_version"  $configFilePath; then
  sed -i "s/php_version:.*/php_version: $1/"  $configFilePath
  echo "php_version: $1" >> $configFilePath

ddev restart

echo "PHP version set to $1"
Bonus: Git hook to switch PHP version

If you’d like to automagically switch PHP version on a certain branch, you can create a git post-checkout hook that will do that for you:


php81_branches=("branch1", "branch2")
currentBranchName=$(git rev-parse --abbrev-ref HEAD)

for item in "${php81_branches[@]}"
    if [ "$item" = "$currentBranchName" ]; then

if [ "$found" = true ]; then
  echo "Setting PHP version to 8.1"
  ddev php-version-local 8.1
  echo "Setting PHP version to 8.0"
  ddev php-version-local 8.0

Container Commands

These commands will run on the container. By default, you’ll have a couple of containers: db and web. The first is for… well, database. The second one is for everything else. It contains PHP, nginx, Node, composer, wp-cli and so on.


By default, ddev composer will run inside the defined composer_root in your .ddev/config.yaml (which is the project root). Most of the time this will work just fine, but there are those edge cases where you’d want to have the command running in your current directory.

Create a file: .ddev/commands/web/custom-composer:


## Description: Run context-composer inside the web container in the root of the project (Use --working-dir= for another directory)
## Usage: context-composer [flags] [args]
## Example: "ddev context-composer install" or "ddev context-composer require foo/bar" or "ddev context-composer --working-dir=my/custom/path require foo/bar"
## ExecRaw: true
## HostWorkingDir: true

composer "$@"

That’s basically it, the whole magic is on this line: HostWorkingDir: true.

Redirect assets to the live server

On large sites, whenever you clone a live site locally, you (probably?) don’t want to clone the assets too. You can configure nginx to proxy_pass those to the live site. Unfortunately, this means that you must eliminate ddev managed nginx file:

  1. Edit .ddev/nginx_full/nginx-site.conf and delete the #ddev-generated line
  2. Scroll down to the bottom of the file & remove these two blocks:
location ~* .(png|jpg|jpeg|gif|ico)$ {
    expires max;
    log_not_found off;
location ~* .(js|css)$ {
    expires -1;
    log_not_found off;

Not sure why this conflicts with the following block, but it does 🤷 Replace it with:

location @proxy {
    # proxy_ssl on;
    # subs_filter_types *;
    proxy_ssl_server_name on;
    proxy_http_version 1.1;
    proxy_set_header Accept-Encoding "";
    proxy_set_header Cache-Control no-cache;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header X-Real-IP $remote_addr;
    add_header X-query-string $is_args$query_string;
    resolver_timeout 5s;

location /wp-content/uploads/ {
    try_files $uri $uri/ @proxy;

location /wp-content/blogs.dir/ {
    try_files $uri $uri/ @proxy;

⚠️Make sure you restart ddev after doing this!

Note that this snippet is for WordPress; for other CMS-es you’d want to replace /wp-content/uploads/ part with the CMS path!


If you’re using PHPStorm, you should also use the DDEV plugin, works pretty much OK. BUT there are some issues.

I like to have my projects organized by their actual URL. Wich usually includes a dot. This doesn’t matter that much in any other area except on remote PHP. It’s a docker thing, so you could ignore the whys, but here is how you can deal with it:

After you install the plugin, it will auto-set-up the PHP CLI interpreter:

Which is super cool, except it is wrong on multiple levels:

  1. The environment variables should not have the dot
  2. The PHP Executable should not have the version.

So you must duplicate the DDEV entry, and have it like so:

While you’re there, make sure you include system environment variables too, otherwise you’ll get some errors and your PHP interpreter will not work.

(I’m not saying that I’ve spent half a day to figure this out, but…)

What is not working – like… at all – is remote Node interpreter. This is a PHPStorm thingy, as can’t be configured like PHP is, to reuse a docker container, so I’ll have to live with it for now.


I’ve been using DDEV for a couple of months now, maybe a little longer. I’m super excited about how everything is working. Sure, there is a learning curve, but is super lean and the community si amazingly helpful.

Oh, about the >20s page loads I mentioned in the begining? Now it’s less than 5 seconds with xdebug enabled.



de către



Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *

Acest site folosește Akismet pentru a reduce spamul. Află cum sunt procesate datele comentariilor tale.

windows apple dropbox facebook twitter