How to debug PHP inside Docker from IntelliJ
How to debug PHP on our IDE when PHP is running on a Docker container (possibly on the same machine)IntelliJ (and of course PHP Storm) are wonderful IDEs, and can help you debug your applications and your servers either locally or remotely.
Since a few releases, IntelliJ can also debug a server running inside a Docker container, possibily running on the same PC/Mac where the IDE is running. But, the nice part is, thanks to TCP/IP this was possible even before Docker integration. It’s just a matter of configuring debugging for a generic remote server..
In this example we’ll see how to debug PHP, but the same principles apply to many other languages.
** Configuring PHP inside Docker**
We assume you have an up and running PHP configuration inside Docker, possibly with an Nginx web server and another container for the DB. THis should be up and working prior to trying to configure debugging.
The first step is to ensure tha PHP (usually PHP-FPM) is configured to have debugging enabled. We will cover debugging using the XDebug extension.
To ckeck that PHP has Xdebug eanbled, you can
* log into the Docker container and do a php -i | grep xdebug
* create a .php
file containing just <?php phpinfo();
and load it in a browser
Just check that you have the key xdebug support => enabled
. If not you have to enable or compile PHP with XDebug.
It depends on the Dockerfile you are using, but usually it has two steps:
1. compiling Xdebug inside PHP directly
1. or adding it as an extension
1. AND enabling via some configuration options.
As to how to enable XDebug in PHP, it’s too Dockerfile-dependant. Many dockerfile for PHP can be configured to have XDebug enabled. Please see the relevant documentation.
Once XDebug is installed and enabled, you have to configure it. To configure it you must ensure that during the docker build
phase, the correct settings for XDebug are copied to the container.
In our Docker-compose directories, there is a xdebug.ini
file that gets copied.
Please see it has the following keys:
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_port=<DEBUG_SERVER_PORT>
xdebug.remote_host=<DEBUG_SERVER_HOST>
This will tell the XDebug module to connect to a debug server on the given host and port. The server will be our beloved IDE IntelliJ. Bear in mind, while debugging PHP remotely, the client is the PHP engine (PHP-FPM usually) while the server is our IDE.
Pick any random free port for your debug server port, i.e. 9999
and get the adress of our machine, as seen from a docker container.
The last point is very important. Evey Docker host can help you. For example on MacOSX (MacOS now) latest releases of Docker have the special docker.for.mac.host.internal
name that always point to the host Mac.
So in our case the configuration looks like:
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_port=9999
xdebug.remote_host=docker.for.mac.host.internal
If you put numeric addresses, please check them from inside the PHP docker container, by pinging the address. If ping
is unavailable, just add this to your Dockerfile a few lines befor the end: RUN apt-get update -yqq && apt-get -y install telnet iputils-ping
, or run the command once inside the container.
If you want, you can set these other two keys:
xdebug.idekey=<AN_IDE_JEY>
xdebug.remote_log=<A_LOG_FILE>
THe first one helps you debug directly without having to start the debugging from the IDE (or debugging PHP not from a web browser, i.e. from a call to PHP originating from mobile app or a IoT device), the second one can help you troubleshoot why the debugging does not start.
Most of the times it’s because the xdebug.remote_host
is wrong.
We hade one case using a well-konwn package for Docker/PHP named Vessel
where Vessel itself overwrote our configuration of the remote host with one it thought it was more correct. In fact it wasn’t. JUst modifying the Vessel script took care of the problem.
So to summarize, our full xdebug.ini
file looks like that:
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_port=9999
xdebug.remote_host=docker.for.mac.host.internal
xdebug.idekey=INTELLIJ
xdebug.remote_log=/var/log/xdebug.log
As usual, do a docker build
of the container, and check when the container is running, that the configuration are correct.
** Configuring IntelliJ/PHPStorm **
This is the tricky part, and involves a few steps:
1. confugure XDebug
1. define a server entry for our docker container
1. define a Run/Debug configuration
1. start debugging
The trickiest part is the 1st, in my opinion.
So..
First we configure XDebug.
Go to the Preferences
menu and look for Languages & Frameworks | PHP | Debug
. In the XDebug section set:
- the Debug port to the one in the
xdebug.ini
file. In our example it’s9999
. - Check just
Can accept external connections
and do not, unless you really need it, check the twoForce break on..
otpions.
This option tells IntelliJ that debugging can also start from the client. It is the only way to debug command line scripts or non web clients, as mobile apps.
Then go to the same Preferences
menu and look for Languages & Frameworks | PHP | Servers
.
Add a new server, give it a name (the name is important), and configure it
- The
Host
andPort
is used when launching the debugger from IntelliJ. If you plan to use this feature, set them correctly, otherwise set them to anything. - Choose
XDebug
- Check
Use path mapping
and define at least one mapping between the source code on you Mac/PC file system, and the path on the Docker container. - Since we use Laravel, we have set up also the path to the
public
directory explicitly. In Laravel, this is the directory where theindex.php
file resides. And the directory that is set as root path innginx
config.
Then click Apply and quit the dialog.
Next we need to configure debugging.
Click the Run
menu and choose Edit configurations...
.
Add (the [+]
icon on the top right) a new PHP web page
and configure it
give it a name. It’s not important.
* Choose the Server
you have set up before.
* Choose a starting URL (it can be anything on your server) and a prefereed browser.
Click OK and save.
Now to test it, on the top toolbar of INtelliJ, near the top right, select the configuration just created.
Then start debugging by pressing the green bug button. This will launch a browser with the url you have set up: http://rotor.local:9980/it?XDEBUG_SESSION_START=11227
Notice the XDEBUG_SESSION_START. If you inspect the cookies of your browser you will see that it has setup a cookie names XDEBUG_SESSION
. This is the key to remote debuggig.
IntelliJ will bring up a Debug panel, and in the variable pane it will tell Waiting for incoming connection with ide key '13326'
.
From now on breakpoints works as expected.
See our other article on how to debug without starting from a browser.