To follow along with this tutorial, make sure that you have Docker installed for your operating system, ideally the latest version, and one of Xdebug’s supported clients. You can find all the instructions you need for installing Docker — regardless of whether you’re running Linux, macOS, or Windows — in the Docker documentation.
For simplicity’s sake, I’ll assume that your Docker Compose
configuration, stored in
docker-compose.yml in the root of the project
directory, looks like the example below.
version: '3' services: webserver: image: nginx:latest ports: - 8080:80 volumes: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf - ./:/var/www/html php: build: ./docker/php/ expose: - 9000 volumes: - .:/var/www/html
You can see that it’s composed of two services:
This is because NGINX, in the
webserver container, is communicating
with PHP, in the
php container, via PHP-FPM,
on port 9000.
The application’s source code, regardless of whether it uses a framework
such as Laravel, Mezzio, or
Symfony or not, is located in the root directory of
the project on the development machine and will be available to the
/var/www/html. Feel free to use whatever application you
prefer to follow along.
If you’d like to learn more about creating a Docker Compose configuration, check out the series that I wrote on it.
php container uses a custom Dockerfile (
to define its build steps, which you can see in the example below. This
is because Xdebug doesn’t come "bundled" with the official Docker Hub
FROM php:7.4-fpm RUN pecl install xdebug \ && docker-php-ext-enable xdebug
However, it only takes two additional steps to install and enable it, as you can see above. These are:
docker-php-ext-enable. This command saves us the hassle of writing a custom shell script.
With Xdebug installed and enabled, we need to enable step
debugging. To do that, create the
two configuration files:
docker/php/conf.d/error_reporting.ini; and the paths if you don’t have
the directory structure set up yet.
To save some time, you can use the following commands to do so.
mkdir -p docker/php/conf.d touch docker/php/conf.d/xdebug.ini touch docker/php/conf.d/error_reporting.ini
docker/php/conf.d/xdebug.ini, add the following configuration to
zend_extension=xdebug [xdebug] xdebug.mode=develop,debug xdebug.client_host=host.docker.internal xdebug.start_with_request=yes
Here’s what the settings do:
developto enable development aids, such as getting better error messages, and
debugto enable step debugging.
yesinstructs Xdebug to always initiate a debugging session.
docker/php/conf.d/error_reporting.ini, add the following
configuration, to enable full error reporting. Always good to know
what’s going wrong, if and when it does.
Then, in the
php service definition in
docker-compose.yml, add the
following two entries to the
- ./docker/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini - ./docker/php/conf.d/error_reporting.ini:/usr/local/etc/php/conf.d/error_reporting.ini
With the ini files created and
docker-composer.yml updated, to have
php container make use of them, restart and rebuild it by running
the following command.
docker-compose up -d --build php
--build flag builds images before starting containers. It’s
essential to use this flag because without it the changes that we made
docker/php/Dockerfile won’t take effect.
Now that we’ve finished configuring the PHP container, we need to configure our client. This will vary, depending on the client that you’re using. For the purposes of this article, I’m using PhpStorm.
It’s default debug settings, which you can find under, use Xdebug’s default values, which we’ve not altered in our configuration. So the only thing that we need to do is to tell it to start listening for PHP Debug connections, by toggling the phone icon in the top menu bar. However, I recommend enabling "Break at first line in PHP scripts" during initial setup, as an extra way of knowing whether step debugging works on first try, when no breakpoints are set.
For the purposes of this guide, I’ll be using PhpStorm (version 2021.1 EAP at the time of writing). Regardless, once you have your IDE or text editor of choice setup and ready to go, open the project code and set a breakpoint. Then, run the code in your browser.
All being well, you should see that the request doesn’t complete in your browser, rather your text editor or IDE takes focus and stops on the breakpoint. You can see an example of this in PhpStorm in the screenshot below.
If your text editor or IDE didn’t stop at the breakpoint that you set, then you’ll have to troubleshoot what might have gone wrong, by consulting Xdebug’s diagnostic log.
To view Xdebug’s diagnostic log, you could add the above code before the breakpoint and reload the page. This is handy when you want to do some quick troubleshooting. When you run the application again, you’ll see output similar to the screenshot below.
The first thing to look for is whether "Step Debugger" is listed as
✔ enabled. If not, then, more than likely, the configuration which we
created isn’t being used by the container.
If the step debugger is enabled, next look at the messages in the "Diagnostic Log" block for any messages prefixed with "[Step Debug]". If you see any of these, try the following steps:
To learn more about a message, click the icon in the "Docs" column, at the far right-hand side of the entry.
Likely a better choice, however, is to enable logging support and then
view the log entries that Xdebug writes there. To do that, you need to
enable logging in Xdebug, to record all file creations issues, step
debugging connection attempts, failures, and debug communication. To do
so, add the highlighted line below to
[xdebug] xdebug.mode=develop,debug xdebug.client_host=host.docker.internal xdebug.start_with_request=yes xdebug.log=/tmp/xdebug.log
You can configure the amount of information that Xdebug writes to the
log file by making use of
xdebug.log_level. It supports six log
levels, which you can see in the table below. By default, it’s set to 7.
Errors in the configuration
Information while connecting
Breakpoint resolving information
After you’ve added the new configuration and saved the file, restart the container using the following command:
docker-compose up -d php
If step debugging is working, you will see two log entries, similar to those below, in Xdebug’s log file, and your text editor or IDE will stop on the breakpoint that you set.
[Step Debug] INFO: Connecting to configured address/port: host.docker.internal:9003. [Step Debug] INFO: Connected to debugging client: host.docker.internal:9003 (through xdebug.client_host/xdebug.client_port). :-)
For more information on configuring and troubleshooting Step Debugging in Xdebug, please refer to Xdebug’s documentation.
To quickly summarise:
Whereas, as many blog posts will attest, before Xdebug 3, it could be quite challenging to set up step debugging when using Docker, in version 3, it’s virtually trivial. I hope that this guide has shown you that it doesn’t have to be challenging anymore and can be a definitive guide on the subject.
If you found this tutorial helpful bookmark it for future reference and please consider sharing it with other developers who may benefit from it!
You can unsubscribe at any time.