Skip to content

Xdebug on Docker

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
  2. Create our Dockerfile
  3. Build our Image
  4. Run our Container

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.

Line 7: Allow access to the container’s port 80.

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 ./

The -t  argument specifies that our image will be named ‘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.

Attempts to avoid initiating debugger sessions with popular browser plugins that use cookies don’t work.  The debug session will always start as long as your have a debugger listening.  However, appending XDEBUG_SESSION_STOP=1  to the URL manually or via browser plugin will successfully tell Xdebug to not start a debugging session.

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.

 

Facebooktwitterredditlinkedin

Published inWeb Development

Be First to Comment

    Leave a Reply

    Your email address will not be published. Required fields are marked *