I've been using Docker for my local environments at Vanilla since 2017.It was a good way to ensure consistent & reproducible developer environmentsand was a marked improvement over what we were running before. We setup a single repositorywith our shared environment (vanilla-docker) andit spread across the company like wildfire.

Instruct XDebug to connect to host.docker.internal for command line execution or whenever “connect back” is not possible. Set PHPIDECONFIG env variable to serverName=localhost. This will tell your PhpStorm which server configuration to use. See next step for details.

The consistency was great, especially as we onboarded various junior developers over the years.Unfortunately in 2018, Apple released MacOS High Sierra, sporting a new filesystem, APFS.

  • Run/debug a php script on docker To verify that everything is working, open the file app/hello-world.php in PhpStorm, right click in the editor pane and choose 'Run'. PhpStorm will start the configured container and run the script. The output is then visible at the bottom of the IDE.
  • Before Docker, Xdebug was relatively straightforward to configure on a platform, in that it was a new set of the php.ini parameters – you’d either just edit the existing php.ini, or load in a custom ini or override.
  • Xdebug is an extension for debugging your PHP code. Magento Cloud Docker provides a separate container to handle Xdebug requests in the Docker environment. Use this container to enable Xdebug and debug PHP code in your Docker environment without affecting your.

This brought one single major regression to our developer environments.

Docker for Mac was Slow!

Docker Xdebug Php

In the beginning, it was completely unusable.Massive CPU spikes would freeze up our machines, and response times were abysmal.

Things got a little better with introduction of a few options when mounting volumes: Delegated & Cached

These along with various improvements in docker for mac made things better,but we still struggled with performance for a long time.It wasn't completely unusable, but we were seeing 3-4 second response times in docker,where we would see 200-300ms response times in local development.

Various Attempts at Fixing it

I tried a few things to speed them up.

  • NFS Volumes - This offered a 30-40% speed improvement but proved to difficult to roll across all of our developers due to the additional configuration required.
  • docker-sync - This tool gave essentially native performance but brought some major drawbacks. I tried 2 times, once in 2019, and again in the beginning of 2020 but these still seemed to hold true.
    • It was very slow to startup. We have a lot of files and directories, and the initial sync would take 10+ minutes with no status indicator. Sometimes it would hang entirely and you'd have to reboot your machine, wipe the containers, and start again.
    • It would stop syncing at random times with no indication. You would notice when you changes suddenly stopped applying. Often the only fix was to wipe the volumes and redo the initial sync. This was particularly evident when checking out and older release and swithing back.
    • The configuration was complicated. Additional commands were required for startup, and the configuration file used a poorly documented syntax for marking excluded directories (a few node_modules directories in particular needed to be excluded in order for things to sync for even short periods of time).
    • Sometimes filesystem permissions wouldn't sync properly. This tended to happen with certain configuration files written by the app.

The Real Problem - XDebug

Many of our developers use XDebug extensively during development and testing.A 2-5x slowdown while running a debug session is not unexpected.

Little did I know that just having the extension installed brings along some significant slowdown.This is amplified in docker, where every System IO call brings with it a lot of overhead due to the virtualization in Docker for Mac.

Removing the XDebug extension had the local sites responding within expected times again.XDebug is really useful though. I didn't want to give it up. Enabling it also couldn't be an onerous activity; I could use XDebug 10-20 times throughout a workday, and having to restart the container would be a chore.

The Solution - 2 PHP-FPM Containers

Cli

The final solution ended up being running 2 PHP-FPM containers.

  1. With a 'production-ish' configuration. This one without XDebug and with a development configuration for OPCache.
  2. With a debugging configuration. This one with XDebug.

Nginx was already used to serve our PHP-FPM processes, so I just updated the configuration to route between them.

Docker Xdebug Php-fpm

The Configs

Nginx Server Config

Debug Php Vscode Remote

This is the bulk of the required configuration. It does the following:

Docker Xdebug Php Download

  • Define the 2 upstreams (php-fpm socket and php-fpm-xdebug socket).
  • Define a few mappings 2 allow switching between the 2 upstreams based on
    • An XDebug cookie.
    • A query parameter of ?XDEBUG_SESSION_START
    • Many possible cookie values used by various browser plugins and IDEs.

Docker Xdebug Phpstorm

Debug PHP config

Phpstorm Docker Xdebug Path Mappings

'Production-ish' PHP config