Master Multi-Stage Xdebug Configurations in Docker

Reading Time: 4 minutes
Optimize PHP development with our easy guide on setting up Xdebug in Docker.
Guide for setting up Xdebug in Docker

Table of Contents


Welcome, innovative tech minds! Are you gearing up to tame the beast that is Docker with Xdebug? Brace yourself because you’re about to embark on a journey toward seamless debugging in multi-stage Docker environments. With this guide, you’ll set up Xdebug like a pro, paving the way for unruffled coding experiences.

What is Xdebug and Why Use It?

In the wilds of coding, Xdebug stands as a beacon of hope. It’s not just debugging; it’s about understanding the inner workings of your PHP scripts. With Xdebug, you can step through your code, inspect variables, and more. When used with Docker, it empowers you to maintain the performance while keeping an eye on potential issues.

Configuring for Peak Performance

The union of Docker and Xdebug is like a dance—it requires rhythm and the right moves. Here’s how you lead:

Mastering the Dockerfile

Your Dockerfile isn’t only a script; it’s a blueprint for your application’s runtime environment. Observing the right conventions is the key to a robust setup. Behold, your Docker blueprint:

					# path: .docker/Dockerfile

FROM php:8.2-fpm-buster as base

FROM base as dev

RUN set -e ; \
    pecl install xdebug-3.2.2; \
    docker-php-ext-enable xdebug;

# copy xdebug settings
COPY .docker/php/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

# discover script
COPY .docker/scripts/ /usr/local/bin/


We start with a common PHP base image, install Xdebug, then ensure its settings are copied over. Notice the second stage named ‘prod’? That’s where we disable Xdebug because, in production, performance is king.

Details in .docker/php/xdebug.ini

Now, for a deep dive into the xdebug.ini file. It’s crucial—it tells Xdebug how to behave.

					# path: .docker/php/xdebug.ini

Each directive has a purpose—setting the mode, defining where to send the output, and others. It’s tailored for precise control.

Multi-Stage Dockerfile Full Breakdown

Your Dockerfile should mirror the stages of your application’s lifecycle, from development to production:

					# path: .docker/Dockerfile

FROM base as ci


For CI, we’ve switched off Xdebug (notice the ENV XDEBUG_MODE=off). It’s all about adjusting each environment’s settings to get that sweet spot of efficiency and functionality.

Setting up the .env file by environment

One key aspect of managing configurations in Docker environments is the utilization of .env files. They serve as a manifest for environment variables, effectively separating configuration from code. Different environments require different settings, and that’s where .env files shine. Let’s break down the significance of each of these files:


The .env.integration file is crafted for your integration environment. Here’s the essence:

					# path: .env.integration

During integration testing, you want performance, not internal insights, hence Xdebug is switched off. If you do need Xdebug for coverage reports, you’d set it to coverage instead of off.


Your local environment is your sandbox. With .env.local, you’re setting up Xdebug to communicate with your IDE:

					# path: .env.local


Here we have a diverse configuration. While Xdebug is off by default, the setup is primed for debugging when you’re ready to inspect your code. You’ve got the host, port, IDE key—all you need for Xdebug to hold a conversation with your IDE, like PHPStorm.


In production, .env.production keeps it simple:

					# path: .env.production


No Xdebug here—performance is the priority, and you want minimal overhead.


Staging mirrors production, but it’s also your dress rehearsal zone. The .env.staging configurations reflect this:

					# path: .env.staging


It’s an echo of production—Xdebug is off but ready to be enabled if you need some last-minute profiling.

With these .env files, remember to restart your Docker containers upon changing Xdebug settings. They won’t take effect until you do. And if you’re ever uncertain about your XDEBUG_CLIENT_HOST, a quick run of the command:

					# shell

make xdebug-discover-host 

This will configure automatically the right IP address for your configuration. Especially helpfull if you are using 

Mastering the art of these environment-specific settings propels your development practices into a realm of precision and control. Whether it’s integration, local development, staging, or production, every environment gets the settings it deserves with .env files.

Integrating GitHub Workflow

Integrating Xdebug with your GitHub workflows streamlines your continuous integration process. It looks something like this:

					# path: github/workflows/unit-tests.yml

    name: Unit tests
    runs-on: ubuntu-20.04
      - name: Run Unit tests
        run: |
          docker run \
            -e XDEBUG_MODE=coverage \
            php vendor/bin/phpunit \
            -c phpunit.xml.dist

This script sets up an environment right on GitHub’s servers to run your tests automatically. It ensures nothing slips through the cracks.

Automating with Makefile Commands

Your Makefile is a vital cog in the machine—it orchestrates how your application is built and tested. Behold, the power in a Makefile:

					# path: Makefile
  $(EXEC_PHP) php -d 'xdebug.mode=off' /usr/bin/composer install --no-scripts

	$(EXEC_PHP) php -d 'xdebug.mode=off' /usr/bin/composer update

	$(EXEC_PHP) php -d 'xdebug.mode=off' /usr/bin/composer dump-autoload

	$(EXEC_PHP) php -d 'xdebug.mode=off' /usr/bin/composer dump-autoload -a

	$(EXEC_PHP) php -d 'xdebug.mode=off' /usr/bin/composer update --lock

    $(EXEC_PHP) bash
	$(EXEC_PHP) bash -c "rm -rf /var/www/data/xdebug/cachegrind*"  

With these snippets, we tell Composer to gracefully yield to our strict Xdebug settings. Further, the xdebug-discover-host pulls its weight by automating host IP discovery.

Unearthing Host IPs with

Ever felt lost choosing the XDEBUG_CLIENT_HOST ? This script has got your back:

					# path: .docker/scripts/

#!/usr/bin/env bash
set -e


if ! ping -c 1 "${HOST}" >/dev/null 2>&1 ; then
    HOST=$(ip route | awk '/default/ { print $3 }')

echo "HOST: $HOST"

if [ -f .env ]; then


This script simplifies your setup. Run it and watch your `.env` file update itself with the correct host.

Configure PhpStorm

A screenshot of phpstorm showing the xdebug configuration

In my case, I configure xdebug to work with port 9100 but the default for it is 9003.

Conclusion and Next Steps

With this hefty guide, you’re now armed to the teeth with knowledge and examples. Set up Xdebug in your Docker environment, and raise your developer flag high! Should you desire a custom fit or hit any snags, our consulting services in API, PHP, DevOps, and others are at your disposal. Let’s elevate your Docker strategy to the highest level!


The information provided here is sourced from the Official Xdebug documentation, Docker official documentation, coupled with a generous sprinkle of hands-on experience.

Chomping at the bit to refine your Docker workflows with Xdebug? Get in touch, and together, we’ll turn potential into excellence.

Share it on:


About the Author

Gary Gitton
Hello, I'm Gary Gitton - an accomplished Software Engineer, Tech Lead, specializing in PHP, API Engineering, DevOps, and Cloud Management. Throughout my career, I've had the privilege to enhance multiple software solutions with my expertise in multiple languages and platforms. I bring my unique blend of technical and business acumen to every project, ensuring efficient, scalable, and innovative outcomes. I'm also passionate about fostering a culture of continuous learning, mentoring developers, and leading projects with agile methodologies. Whether concocting a bespoke API or orchestrating cloud environments, I'm committed to delivering quality and excellence. Let's connect and transform your vision into a digital reality.

You might also like