DEV Community

Cover image for Install Xdebug with PIE: Setup, Modes and Production
devTalk
devTalk

Posted on Originally published at dev-talk.com

Install Xdebug with PIE: Setup, Modes and Production

Xdebug is the one PHP extension almost everybody installs, and for years the command was pecl install xdebug. The Xdebug install page now says that PECL and its website are deprecated, and it lists pie install xdebug/xdebug as the recommended route. The new command is easy. The part that bites people later is leaving Xdebug switched on where it should not be, because it makes PHP a lot slower. This guide covers both: installing it with PIE, and making sure it only runs where you want it.

Before you start

  • PIE installed. If you do not have it yet, the PIE installation guide covers it in a few minutes.
  • Build tools and PHP development files on Linux and macOS, because PIE compiles Xdebug from source. Run pie check-build-tools to see what is missing. On Debian or Ubuntu you also need the dev package for your PHP version, for example php8.3-dev, which provides phpize.
  • Windows downloads a prebuilt binary instead, so no compiler is needed.

Install Xdebug

pie install xdebug/xdebug
Enter fullscreen mode Exit fullscreen mode

PIE downloads the source, runs phpize, configure and make, copies xdebug.so into your extension directory and tries to enable it. On an Ubuntu 24.04 test machine with PHP 8.3 the last lines looked like this:

Install complete: /usr/lib/php/20230831/xdebug.so
Enabled extension xdebug in the INI file /etc/php/8.3/mods-available/xdebug.ini
Successfully asserted that extension xdebug is loaded in runtime.
Extension xdebug/xdebug:3.5.3 is enabled and loaded in /usr/bin/php8.3
Enter fullscreen mode Exit fullscreen mode

You do not edit php.ini yourself. PIE wrote the zend_extension=xdebug line, in a file with priority 90 so it loads after OPcache, which is the order the Xdebug docs ask for. The exact file depends on your system: here it was mods-available/xdebug.ini linked into conf.d as 90-xdebug.ini, and on other setups PIE adds the line to php.ini. The Xdebug docs also say to ignore the old PECL prompt that suggests adding extension=xdebug.so, since that causes problems.

Check that it loaded

php -v
# ... with Xdebug v3.5.3, Copyright (c) 2002-2026, by Derick Rethans

pie show
# Loaded PIE extensions:
#   xdebug:3.5.3 (from xdebug/xdebug:3.5.3)
Enter fullscreen mode Exit fullscreen mode

If you run PHP-FPM or Apache, restart them afterwards. The command line and FPM often use different ini directories, so check the one that matters with php --ini (CLI) or a page that calls xdebug_info().

Pick a mode, because the default is not what you want

Xdebug does nothing useful until you tell it which features to turn on, and each one has a cost. The setting is xdebug.mode. On Xdebug 3.5.3 the default I measured was develop, which is not what most people expect.

Mode What it gives you
debug Step debugging from your IDE (breakpoints, stepping)
develop Better var_dump() output and stack traces
coverage Code coverage for PHPUnit
profile Profiler output for tools like KCachegrind
trace Function-call trace files
gcstats Garbage collector statistics
off Loaded, but every feature disabled

You can combine them with commas (debug,develop). You can also override the ini file for a single command with the XDEBUG_MODE environment variable, which is handy in CI:

XDEBUG_MODE=coverage vendor/bin/phpunit
Enter fullscreen mode Exit fullscreen mode

I checked that XDEBUG_MODE=coverage reports coverage as the only active mode through xdebug_info("mode"), so the variable really does win over the ini value.

Set up step debugging

Put your own settings in a separate file so a later pie upgrade never touches them. For example /etc/php/8.3/cli/conf.d/99-xdebug-local.ini:

xdebug.mode = debug
xdebug.start_with_request = trigger
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9003
Enter fullscreen mode Exit fullscreen mode

Port 9003 is Xdebug 3's default (Xdebug 2 used 9000), so make sure your IDE listens on 9003. start_with_request = trigger means Xdebug only connects when you ask it to, instead of on every single request. For a CLI script, the trigger is an environment variable:

XDEBUG_TRIGGER=1 php artisan my:command
Enter fullscreen mode Exit fullscreen mode

To prove the connection works without an IDE, I started a plain socket listener on port 9003 and ran a script both ways. With trigger and no XDEBUG_TRIGGER, nothing connected. With XDEBUG_TRIGGER=1, Xdebug connected and sent its DBGp init packet. For web requests the Xdebug docs describe a browser trigger (a cookie or query parameter), which I did not test here. In Docker, client_host usually has to point at the host machine rather than 127.0.0.1, for example host.docker.internal. That one is a common setup, not something I ran.

The cost of leaving it on

This is the part that matters for production. I ran a call-heavy script (a recursive Fibonacci, about seven million function calls) on the same PHP 8.3 build:

Setup Time
Xdebug not loaded about 0.09 s
Loaded, xdebug.mode=off about 0.09 s
xdebug.mode=debug about 1.2 s
xdebug.mode=develop about 1.6 s

That script is close to a worst case, since it does nothing except call functions. A real application spends time in databases and I/O, so the slowdown will be smaller, and you should measure yours. But the pattern is clear: loaded with mode=off costs almost nothing, while a live mode costs a lot. That is why a forgotten Xdebug on a server is such a common cause of "why is production so slow".

Keep Xdebug out of production

Use more than one safeguard, in this order of preference.

1. Do not install it where it is not needed

Production images and servers should simply not have it. The simplest rule is to install it only in your development image or on developer machines.

2. Declare it as a dev dependency

PIE can read the extensions your project needs from Composer. If Xdebug is in require-dev:

{
    "require": { "php": "^8.3" },
    "require-dev": { "ext-xdebug": "*" }
}
Enter fullscreen mode Exit fullscreen mode

then this installs project extensions, skipping the dev-only ones:

pie install-extensions-for-project --no-dev
Enter fullscreen mode Exit fullscreen mode

In PIE 1.5.2 the help text for --no-dev says it excludes extensions declared in the root package's require-dev. I tested it with a project whose only extension requirement was ext-xdebug in require-dev: with --no-dev PIE reported "Finished checking extensions" and installed nothing, and without the flag it built and installed Xdebug. Two practical notes: the command reads composer.lock and failed with "No lockfile found" in a project that had none, and if PIE cannot work out which package provides an extension you can name it with --select=xdebug=xdebug/xdebug.

3. If it must be present, turn it off

xdebug.mode = off
Enter fullscreen mode Exit fullscreen mode

As the table above shows, that brings the cost back to roughly nothing. It is a fallback, though. Not loading the extension at all is still safer, because Xdebug's debugger and develop output are things you never want reachable on a public server.

Upgrade and remove it

pie upgrade                    # upgrades all PIE-installed extensions
pie uninstall xdebug/xdebug    # disables and removes it
Enter fullscreen mode Exit fullscreen mode

On my test machine uninstall deleted xdebug.so and commented out the load line (; zend_extension=xdebug ; removed by PIE). The ini file and the conf.d link stayed, which is harmless. php -v no longer showed Xdebug.

Common problems

  • phpize: not foundor build failures. Install the build tools and your PHP version's dev package, then run pie check-build-tools. PIE can also install missing tools for you with --auto-install-build-tools.
  • Installed, butphp -vdoes not show Xdebug. You probably installed into a different PHP than the one you run. Point PIE at the right one with --with-php-config=/usr/bin/php-config8.3, then restart PHP-FPM or Apache.
  • Xdebug twice. If you used pecl install before, search your ini files for a second zend_extension line (grep -r xdebug /etc/php) and remove the old one.
  • A new PHP version. The composer.json in the Xdebug 3.5.3 tag declares php: >=8.0,<8.6. So that release is not meant for PHP 8.6 builds, and I did not test what PIE does if you try. Check before you upgrade PHP; I covered what is changing in PHP 8.6.

Frequently asked questions

Is pecl install xdebug still working?

The Xdebug docs still list it as the legacy method, and they say PECL is deprecated. The PECL deprecation is a recommendation change, so existing setups keep working, but new setups should use PIE.

Does Xdebug slow PHP down if mode is off?

In my test, no measurable difference. Any other mode adds overhead, and the more function calls your code makes, the more you notice it.

Which port does Xdebug 3 use?

9003 by default. Xdebug 2 used 9000, so an old IDE configuration is a frequent reason a fresh install "does not connect".

References


Originally published on DEV Talk.

Top comments (0)