PHP Xdebug 3 Configuration for Modern IDEs

Khimananda Oli 7 min read Web Development
PHP Xdebug 3 Configuration for Modern IDEs

By Khimananda Oli | Last reviewed: August 2026

Debugging PHP applications often fails not because of code complexity, but because of misconfigured tooling. Getting PHP Xdebug 3 configuration for modern IDEs right is the difference between guessing at variable states and inspecting them precisely in real time. Whether you are running a local LAMP stack or a containerized Laravel environment, the protocol has changed significantly since version 2, requiring updated INI directives and IDE listener settings. This guide provides the exact configurations needed to establish a stable debug session in 2026.

How does PHP Xdebug 3 configuration for modern IDEs differ from version 2?

Xdebug 3 was a complete rewrite focused on performance and configurability, breaking backward compatibility with version 2. If you are migrating legacy configs or following outdated tutorials from before 2024, your debugger will silently fail. The most critical change is the replacement of the monolithic xdebug.remote_enable with granular modes. In production-like environments, this distinction matters immensely; enabling profiling or tracing accidentally can degrade performance by orders of magnitude.

PHP RuntimeXdebug 3 Extensionmode=debugstart_with_request=yesDBGp ProtocolPort 9003 (TCP)Modern IDEVS Code / PhpStormPath MappingsBreakpoint Engine
Xdebug 3 initiates an outbound TCP connection to the IDE listener on port 9003 using the DBGp protocol

The default port has also shifted from 9000 to 9003 to avoid conflicts with PHP-FPM. While you can revert this, sticking to 9003 is now the standard convention across Ubuntu PHP installations and Docker images. Another frequent stumbling block is the removal of xdebug.remote_connect_back. This directive was a security risk and has been replaced by explicit host configuration. In my experience auditing development environments, teams that rely on auto-detection often face intermittent failures when working across VPNs or WSL2; explicit configuration eliminates this class of bugs entirely.

How do I configure Xdebug 3 INI settings for local development?

For a native local installation, your php.ini or a dedicated 20-xdebug.ini file needs only four directives to cover 90% of debugging scenarios. Avoid copying entire configuration blocks from older guides; minimalism prevents side effects.

; /etc/php/8.4/mods-available/xdebug.ini
zend_extension=xdebug.so

xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
xdebug.client_host=127.0.0.1
xdebug.log=/tmp/xdebug.log
xdebug.idekey=VSCODE

Understanding each directive prevents future headaches:

  • xdebug.mode: Accepts a comma-separated list. Use debug for step debugging. Add develop if you want enhanced var_dump() output and stack traces. Never enable profile or trace unless actively analyzing performance, as they generate massive I/O overhead.
  • xdebug.start_with_request: Set to yes to attempt a debug session on every request. In production, use trigger and rely on browser extensions or query parameters (?XDEBUG_TRIGGER=1) to activate debugging on demand. For local dev, yes saves you from managing trigger cookies.
  • xdebug.client_host: The IP where your IDE listens. For local setups, this is always 127.0.0.1. Do not use localhost, as IPv6 resolution (::1) can cause connection timeouts on some systems.
  • xdebug.log: Essential for troubleshooting. If the IDE doesn't break, check this file first. It records connection attempts, protocol errors, and path mapping failures. Ensure the directory is writable by your web server user.

After updating the configuration, restart your PHP-FPM service or Apache/Nginx. Verify the extension loaded correctly by running php -m | grep xdebug or checking phpinfo(). If the module appears but debugging fails, the log file specified in xdebug.log is your primary diagnostic tool. Many developers skip this step and waste hours guessing at network issues when the log explicitly states "Could not connect to client."

How do I set up Xdebug 3 in Docker with VS Code or PhpStorm?

Containerized environments introduce network isolation that breaks naive Xdebug configurations. The PHP process inside the container cannot reach 127.0.0.1 on your host machine. You must bridge this gap using special DNS names and path mappings. This is where most Docker Laravel setups encounter friction.

Docker ContainerPHP + Xdebug 3client_host=host.docker.internal(Gateway to Host)/var/www/htmlNetwork BridgePort 9003 ForwardHost MachineIDE Listener :9003Path Mapping:/var/www/html →/home/user/projectBreakpoints Active
Docker Xdebug flow requires host.docker.internal and explicit path mapping between container and host filesystems

Docker-specific Xdebug INI configuration

Your Dockerfile or docker-compose override should inject these settings. Never bake debug config into production images; use a separate xdebug.ini mounted only in development services.

# docker-compose.dev.yml
services:
  app:
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - XDEBUG_MODE=debug
      - XDEBUG_CONFIG="client_host=host.docker.internal start_with_request=yes"

On Linux hosts, host.docker.internal requires the extra_hosts directive shown above. macOS and Windows Docker Desktop handle this automatically. If you are running WSL2, verify that your IDE firewall allows inbound connections on port 9003 from the WSL virtual adapter.

IDE path mapping essentials

The debugger matches breakpoints by comparing absolute file paths. Inside the container, your code lives at /var/www/html. On your host, it might be /home/khimananda/projects/laravel-app. Without mapping, the IDE receives a breakpoint hit for an unknown file and ignores it.

  • VS Code: In .vscode/launch.json, add "pathMappings": { "/var/www/html": "${workspaceFolder}" } inside your configuration object.
  • PhpStorm: Navigate to Settings → PHP → Servers. Add a server matching your container's hostname, then map the local project root to /var/www/html. Enable "Use path mappings" for all requests.

A common mistake is mapping the wrong directory level. Map the exact root where your index.php resides, not a parent folder. Test this by setting a breakpoint in a known entry point and checking the Xdebug log for "resolved breakpoint" messages.

Why is my Xdebug breakpoint not triggering in 2026?

When configuration looks correct but debugging fails, the issue usually falls into one of three categories: network blocking, path mismatches, or mode conflicts. Systematic elimination beats random tweaking.

SymptomLikely CauseFix
No connection attempt in logxdebug.mode missing or misspelledVerify php -i | grep xdebug.mode returns debug
Connection refused in logFirewall blocking port 9003 or wrong client_hostCheck UFW/firewall rules; confirm IDE listener is active
Connected but no breakpoint hitPath mapping mismatchCompare server path in log vs IDE mapping; adjust case sensitivity
Breakpoint hits wrong lineOPcache serving stale bytecodeDisable OPcache in dev or set opcache.validate_timestamps=1
Session starts then drops immediatelyIDE key mismatch or timeoutAlign xdebug.idekey with IDE config; increase xdebug.connect_timeout_ms

Always consult the Xdebug log before changing configurations. A log entry like I: Connecting to configured address 'host.docker.internal:9003'... followed by E: Could not connect confirms a network issue. Conversely, I: Connected to client followed by silence indicates a path mapping problem. For teams working with Laravel Sail, remember that Sail abstracts some Docker networking; use sail debug instead of manually configuring Xdebug to leverage built-in presets.

Another subtle issue in 2026 involves PHP 8.4's JIT compiler. When JIT is enabled, Xdebug may fail to intercept certain optimized code paths. If you encounter inconsistent breakpoint behavior specifically with JIT-enabled builds, disable JIT temporarily during debug sessions by setting opcache.jit=disable. This trade-off is acceptable in development where correctness trumps raw execution speed.

Streamline Your PHP Debugging Workflow Today

Correct PHP Xdebug 3 configuration for modern IDEs transforms debugging from a frustrating chore into a precise engineering practice. Start with the minimal INI settings, validate connectivity through logs, and only then layer in IDE-specific path mappings. Resist the urge to copy-paste bloated configurations; understanding each directive ensures your setup survives framework upgrades and infrastructure changes. If your team struggles with persistent debugging issues across containerized environments or needs help establishing standardized development workflows, reach out to discuss your specific setup.

Frequently Asked Questions

Add zend_extension=xdebug.so to php.ini and set xdebug.mode=debug, xdebug.start_with_request=yes, and xdebug.client_port=9003. Restart PHP-FPM or your web server, then configure the VS Code PHP Debug extension to listen on port 9003.

Port 9003.

Xdebug 3 replaced remote_enable with xdebug.mode. You must explicitly set xdebug.mode=debug in your configuration file. Legacy settings are silently ignored, causing breakpoints to fail without error messages during modern IDE setup.

Set xdebug.client_host=host.docker.internal and xdebug.client_port=9003 inside the container. In PhpStorm, map the server path to your local project directory. Ensure the container network allows outbound connections to the host on port 9003 for debugging sessions.

Yes. Sail includes Xdebug 3 by default. Set SAIL_XDEBUG_MODE=debug and SAIL_XDEBUG_CONFIG="client_host=host.docker.internal" in your .env file. Run sail up -d and configure your IDE to listen on port 9003 with proper path mappings for the /var/www/html directory.

off, develop, coverage, debug, gcstats, profile, and trace.

Run php -m | grep xdebug or check phpinfo() output for the Xdebug section. Confirm the version is 3.x and that xdebug.mode shows your intended value. If missing, check extension loading order and ensure no conflicting zend_extension directives exist in included config files.

Common causes include mismatched client_port settings, missing path mappings, or xdebug.mode not including debug. Check the VS Code debug console for connection errors. Verify firewall rules allow port 9003 and that PHP process has network access to reach the IDE listener.

No. Never enable Xdebug in production. It severely impacts performance and exposes application internals. Use xdebug.mode=off or remove the extension entirely. Restrict Xdebug to development and staging environments only, and ensure configuration files are excluded from deployment artifacts and container images.

Right-click a breakpoint in your IDE and add a condition expression using valid PHP syntax. Xdebug 3 evaluates this server-side before pausing execution. This reduces overhead compared to unconditional breakpoints in loops. Ensure xdebug.mode includes debug for conditional evaluation to function properly.

Nothing directly replaces it. Xdebug 3 removed this insecure feature entirely. You must explicitly configure xdebug.client_host instead. For dynamic environments like Docker, use host.docker.internal or configure service discovery. This change improves security by preventing unauthorized debugging connections from arbitrary clients.

Set xdebug.mode=profile and xdebug.output_dir=/tmp/xdebug. Trigger requests through your browser or CLI. Analyze generated cachegrind files using tools like KCachegrind or Webgrind. Remember profiling adds significant overhead, so only enable temporarily during performance investigation sessions in development environments.

Yes. Configure xdebug.mode=debug and set xdebug.start_with_request=trigger. Add XDEBUG_TRIGGER=1 environment variable when running phpunit. Your IDE will intercept test execution at breakpoints. This works identically for Pest tests and other PHP testing frameworks using standard PHPUnit runners.

Enable xdebug.log=/tmp/xdebug.log to capture connection attempts. Check if client_host resolves correctly and port 9003 is reachable. Verify no firewall blocks outbound traffic from PHP. For Docker, confirm host.docker.internal DNS resolution. Review logs for specific error codes indicating network or permission issues.

Significant. Even with xdebug.mode=off, the extension adds 10-20% overhead due to hook registration. With debug mode active, expect 5-10x slower execution. Always disable completely in production and benchmark-critical staging environments. Remove the extension entirely rather than just setting mode to off for optimal performance.