This article walks through setting up Xdebug on a Docker container running Apache with PHP 7.1. If you don’t already have it installed, get Docker for your platform here. Some familiarity with the command line is assumed.
Here’s the steps we’ll take:
1. Create our Configuration Files
Let’s create a directory where we’ll store the files that we’re creating. We’ll call it php71-apache .
The first file is optional. It’s a php.ini file. This one-liner will keep PHP from complaining about setting a time-zone. Use your appropriate time zone here. Here’s the contents.
date.timezone = "America/New_York"
The second configuration file is for Xdebug. Here’s our xdebug.ini file contents.
zend_extension = xdebug.so xdebug.remote_enable = 1 xdebug.remote_log = /tmp/xdebug.log
2. Create our Dockerfile
Now that our configuration files are complete. Let’s move on to creating our Dockerfile .
FROM php:7.1-apache RUN docker-php-ext-install mysqli && a2enmod rewrite && yes | pecl install xdebug COPY php.ini /usr/local/etc/php/ COPY xdebug.ini /usr/local/etc/php/conf.d EXPOSE 80
Here’s a rundown of our Dockerfile .
Line 1: Specifies the base image from which we’re building our custom image. This is the official PHP Apache image.
Line 2: Runs a script that will install the mysqli PHP extension. More information about installing PHP extensions in containers can be found on the PHP repository page for Docker under the How to install more PHP extensions section.
Line 3: We enable the rewrite Apache module because most of my projects need it.
Line 4: Install the Xdebug extension via PECL.
Line 5: (optional) Copy our php.ini file to the image.
Line 6: Copy the xdebug.ini file to the image.
3. Build our Image
Now that our configuration is all set up, it’s time to build the image. From within the php71-apache directory, run the following command.
docker build -t php71-apache ./
4. Run our Container
Our image is all setup and ready for us. Let’s spin her up! Here’s the command to do it. Change the location of your website code as needed.
docker run -d --name php71-apache-example -v $HOME/Sites/myexamplesite.com/htdocs:/var/www/html -p 8080:80 -e XDEBUG_CONFIG="remote_host=$(ipconfig getifaddr en0)" php71-apache
This is a little complicated so we’ll step through each bit.
- First we tell docker that we want to run a container with the run sub-command.
- The -d argument says that the container is going to run in detached mode, meaning we don’t need to interact with it or keep it running in the foreground.
- We’re going to call our container php71-apache-example with the –name argument.
- The –v argument tells docker to mount a directory in our home directory called Sites/myexamplesite.com/htdocs to the /var/www/html directory in the container. Both the host and the container will have access to these files at the same time.
- Next, the -p argument says that our host machine will listen for requests on port 8080 and forward those requests to port 80 of the container. You can use port 80 on the host if you don’t have another web server running on your computer using that port.
- The -e argument allows us to create environment variables used in the container. Here, we’re setting the XDEBUG_CONFIG environment variable. This allows us to pass any Xdebug parameters through the environment. In this case, we tell Xdebug the IP address to connect back to; in other words, the IP of your machine where the debugger is listening. I use command substitution to grab my IP address from my Mac. You can manually place your IP address here.
- Finally, we tell Docker to use the php71-apache image we created in the build step above.
Your newly created container should be running and accessible at http://localhost:8080 .
Using Docker Compose
Instead of a complicated run command as given above, we can store all the details in a docker-compose.yml file and use the docker-compose command to run the container instead of docker run . Create a docker-compose.yml file in your php71-apache directory with the contents below. You’ll want to change your volume location and IP address, of course.
version: "2" services: php: container_name: php71-apache-example volumes: - /Users/larry/Sites/myexamplesite.com/htdocs:/var/www/html ports: - 8080:80 build: ./ image: php71-apache environment: XDEBUG_CONFIG: "remote_host=192.168.1.156"
Now, from within the same directory, run the following
docker-compose up -d
Unexpected Xdebug Remote Host Behavior
We could specify the IP address Xdebug connects to in our xdebug.ini file with the xdebug.remote_host directive.
The downside of using xdebug.remote_host in xdebug.ini to specify our IP address is that if it our IP changes for any reason, we have to login to the container, change the xdebug.ini file and restart the container. It’s not a huge issue but when using Docker, we avoid making any changes to containers that aren’t done through configuration.
On the other hand, there’s an undocumented side effect of using the XDEBUG_CONFIG environment variable vs. using the configuration file to set the remote host. When using the environment variable to set the IP address, PHP will try to connect to your debugger on each page request without being initiated by a browser plugin or other trigger. This is the same behavior as if you set xdebug.remote_autostart=1 . You can read more about this configuration item here.
A way to around inconsistent xdebug.remote_host behavior is to write a wrapper script that uses sed to replace the IP address in the docker-compose.yml file and then call docker-compose up -d .
If you’re lucky enough to be using a Mac, you can simply add the following line to your xdebug.ini file instead of worrying about any environment variables:
xdebug.remote_host = docker.for.mac.localhost
I hope you find this article helpful. If so, please share! Have a suggestion to make this article better? Let me know in a comment below.