Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

PHP Xdebug 3 Configuration for Modern IDEs

By Kokil Thapa | Last reviewed: September 2026

PHP Xdebug 3 configuration for modern IDEs changed sharply from Xdebug 2. You no longer scatter a dozen mystery directives across three ini files and hope PhpStorm connects. Xdebug 3 uses a single mode switch, defaults to port 9003, and expects your IDE to initiate the debug session. That sounds simpler until Docker, WSL2, or a mismatched PHP binary on Ubuntu breaks the handshake. This guide walks through a working setup for VS Code, PhpStorm, and containerised Laravel apps on PHP 8.3 through 8.5.

I've spent too many afternoons on Ubuntu PHP server setups where the app ran fine but breakpoints never fired. The fix is almost always ini placement, not the IDE. Start with one verified PHP binary, one ini file, and one listening IDE port before you touch framework code.

How Do You Configure PHP Xdebug 3 for Modern IDEs?

Xdebug 3 splits concerns into modes. You pick what you need instead of loading every feature at once. For daily IDE work, debug is the only mode you require. Add develop if you want improved var_dump() output via xdebug.mode=debug,develop.

Official reference: the Xdebug step debugger documentation lists every directive. Treat that page as the source of truth when a release adds new settings.

Install Xdebug on Ubuntu with the correct PHP version

Match the Xdebug build to the exact PHP binary your web server uses. A common mistake is installing for CLI while Apache or PHP-FPM loads a different version. On Ubuntu 22.04 or 24.04 with PHP 8.4:

sudo apt install php8.4-xdebug
php8.4 -v
php8.4 -m | grep -i xdebug

Prefer PECL when you need a specific Xdebug patch level across multiple PHP versions side by side:

sudo pecl install xdebug
php8.4 -i | grep "Loaded Configuration File"
php8.4 -i | grep "Scan this dir"

Place overrides in a dedicated drop-in such as /etc/php/8.4/mods-available/xdebug.ini rather than editing the main php.ini. That keeps upgrades clean and mirrors how I configure production servers alongside OPcache tuning for production.

Minimal ini block that works in 2026

Copy this baseline into your Xdebug ini file. Adjust the client host section for your environment.

zend_extension=xdebug.so

xdebug.mode=debug
xdebug.start_with_request=trigger
xdebug.client_host=127.0.0.1
xdebug.client_port=9003
xdebug.idekey=VSCODE

xdebug.log=/tmp/xdebug.log
xdebug.log_level=7

Reload the SAPI you actually debug through — not only CLI:

sudo systemctl reload php8.4-fpm
sudo systemctl reload apache2

Confirm the loaded values:

php8.4 -i | grep xdebug.mode
php8.4 -i | grep xdebug.client
PHP Xdebug 3 IDE Debug FlowBrowserXDEBUG triggerPHP-FPMXdebug 3 modePort 9003DBGp protocolIDE ListenerVS Code / PhpStormSession Steps1. IDE listens on 90032. Request carries XDEBUG_SESSION3. Xdebug connects to client_host4. Breakpoint hit — execution pauses
PHP Xdebug 3 configuration for modern IDEs: the IDE listens; PHP initiates the outbound connection on port 9003.

What PHP Xdebug 3 Configuration Do Modern IDEs Like VS Code Need?

Visual Studio Code remains the most common free IDE in PHP teams. Install the PHP Debug extension by Xdebug (publisher: xdebug). Create .vscode/launch.json at your project root:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for Xdebug",
      "type": "php",
      "request": "launch",
      "port": 9003,
      "pathMappings": {
        "/var/www/html": "${workspaceFolder}"
      },
      "xdebugSettings": {
        "max_data": 65535,
        "show_local_vars": 1
      }
    }
  ]
}

Path mappings are non-negotiable in Docker or Vagrant. The left side must match the absolute path inside the container. The right side is your local checkout. One character wrong and breakpoints show as unverified grey dots.

Trigger debugging without leaving Xdebug always on

Never run xdebug.start_with_request=yes on shared staging servers. It adds overhead and opens debug sessions you did not intend. Use trigger mode and activate per request:

  • Browser extension: Xdebug Helper for Chrome or Firefox — set IDE key to VSCODE or PHPSTORM.
  • Query string: append ?XDEBUG_SESSION_START=VSCODE to any URL.
  • Cookie: set XDEBUG_SESSION=VSCODE for the session duration.
  • CLI: XDEBUG_SESSION=1 php artisan migrate for one-off Artisan debugging.

For Laravel queue workers, pass the env var in your supervisor unit or use php artisan queue:listen from a terminal where you have already exported it. Background workers do not read browser cookies.

PhpStorm-specific PHP Xdebug 3 settings

PhpStorm still dominates agency workflows. Open Settings → PHP → Debug. Set Xdebug port to 9003 — not 9000. Enable Can accept external connections. Under PHP → Servers, add your vhost with path mappings identical to VS Code.

PhpStorm listens automatically when you click the phone icon. Match xdebug.idekey in ini to the IDE key field if you use multiple developers on one machine. On a Laravel Livewire booking project I maintain, path mapping between Homestead and macOS was the only blocker after a PHP 8.4 upgrade.

Which PHP Xdebug 3 IDE Settings Fix Docker and WSL2 Connection Problems?

Containerised apps introduce a network hop. Inside the container, 127.0.0.1 points to the container itself — not your laptop. That single fact causes most "Xdebug never connects" tickets.

Docker Compose configuration

Add environment variables or ini overrides so Xdebug reaches the host:

services:
  app:
    environment:
      XDEBUG_MODE: debug
      XDEBUG_CONFIG: "client_host=host.docker.internal client_port=9003 start_with_request=trigger"
    extra_hosts:
      - "host.docker.internal:host-gateway"

On Linux without Docker Desktop, replace host.docker.internal with your bridge IP — often discoverable via ip addr show docker0. Some teams hard-code 172.17.0.1 but verify rather than assume.

WSL2 on Windows

When PHP runs inside WSL2 and VS Code runs on Windows, set xdebug.client_host to the Windows host IP from /etc/resolv.conf (the nameserver line). That IP changes after sleep or VPN connect. A small shell alias to refresh it saves repeated frustration.

Docker Xdebug 3 Network TopologyHost MachineIDE on port 9003Docker Bridge172.17.0.0/16PHP ContainerXdebug clientOutbound DBGp connectionclient_host must reach host IPWrong: 127.0.0.1 inside container loops back to container only
PHP Xdebug 3 in Docker requires client_host aimed at the host gateway, not localhost inside the container.

Enable xdebug.log temporarily when connections fail. Tail it during a request:

tail -f /tmp/xdebug.log

Look for "Connection refused" or "Time-out". Cross-check against PHP-FPM pool settings if requests never reach PHP at all. Firewalls on port 9003 are rare locally but common on remote dev VMs managed through Linux server administration contracts.

How Does PHP Xdebug 3 Configuration Differ From Xdebug 2 in Modern IDEs?

If you learned debugging on Xdebug 2, your muscle memory will betray you. Several directives were renamed or removed. IDEs expect the new defaults.

SettingXdebug 2Xdebug 3Notes for IDEs
Enable debugxdebug.remote_enable=1xdebug.mode=debugModes are comma-separated flags
IDE port9000 default9003 defaultUpdate every launch.json and firewall rule
Host directivexdebug.remote_hostxdebug.client_hostSame purpose, new name
Autostartxdebug.remote_autostartxdebug.start_with_requestUse trigger in shared envs
IDE keyxdebug.idekeyxdebug.idekeyUnchanged — match browser extension
Profilerxdebug.profiler_enablexdebug.mode=profileSeparate mode — do not mix with debug daily

The port change alone breaks more setups than any other single line. Search your repo for 9000 in docker-compose, nginx configs, and old wiki pages. Replace with 9003 everywhere.

Profiler and trace modes now sit behind the same mode switch. For performance work, spin up xdebug.mode=profile briefly rather than leaving debug on production-like staging. Pair profiling sessions with PHP memory limit analysis when you chase slow endpoints.

Xdebug 2 vs Xdebug 3 for IDEsXdebug 2remote_enable=1Port 9000remote_autostartMany toggles always onHeavier default overheadXdebug 3mode=debugPort 9003start_with_requestExplicit mode flagsLower idle overheadMigrate IDE listeners and ini together
Modern IDE PHP debugging requires Xdebug 3 mode syntax and port 9003 — not legacy remote_* directives on port 9000.

What PHP Xdebug 3 Configuration Checklist Works for Laravel and Symfony Projects?

Framework projects add layers — front controllers, public/ docroots, and env-based config. The Xdebug ini still lives at the PHP layer, but your IDE must map into public/index.php entry paths correctly.

Laravel 12 or 13 on PHP 8.3+

  1. Install Xdebug for the PHP-FPM version serving the site — verify with php-fpm8.4 -i if needed.
  2. Set trigger mode; avoid autostart on shared Homestead or Sail instances.
  3. Map container /var/www/html to your local clone in launch.json.
  4. Place breakpoints in route closures, controllers, or jobs — not in compiled Blade views.
  5. Run php artisan config:clear only when debugging config cache issues; Xdebug itself does not need it.

Laravel Sail ships a optional Xdebug install path. If you use Sail, follow its documented SAIL_XDEBUG_MODE variables rather than hand-editing container ini. For custom stacks, mirror patterns from modern Laravel architecture and keep dev tooling out of production deploy artefacts.

Symfony 8.1 and multi-env setups

Symfony CLI can proxy requests and inject its own PHP binary. Confirm which binary serves symfony server:start with symfony php -i | grep xdebug. Symfony 8.1 requires PHP 8.4.1 minimum — align Xdebug with that exact build.

PHPUnit and Pest tests also respect Xdebug when triggered. In CI pipelines, disable Xdebug entirely for speed. Use PHPStan static analysis and Pint or CS Fixer for automated checks that do not need a debugger attached.

When to skip Xdebug entirely

Not every problem deserves a breakpoint. Log channels, Laravel Telescope, or Symfony Profiler cover many production-like issues faster. Reach for Xdebug when state mutates across middleware, payment callbacks fail silently, or queue serialization behaves oddly — cases I've hit on Laravel eCommerce platforms.

Xdebug 3 Troubleshooting Decision TreeBreakpoint not hit?Check path mapsContainer vs localVerify PHP binaryFPM vs CLI iniRead xdebug.logConnection errorsFix launch.json pathsReload correct FPMFix client_host IPStill stuck? Confirm IDE listens on 9003and XDEBUG_SESSION trigger is active
Systematic PHP Xdebug 3 configuration checks for modern IDEs: path maps, PHP binary, log file, then network.

How Do You Keep PHP Xdebug 3 Off Production While Debugging Locally?

Xdebug must not load on production web nodes. It adds measurable latency and exposes internal structure. Separate ini files per environment or use conditionally loaded modules.

# Production — disable entirely
# /etc/php/8.4/fpm/conf.d/99-xdebug-prod.ini
; zend_extension=xdebug.so commented out or package removed

# Local dev only
xdebug.mode=debug
xdebug.start_with_request=trigger

On Deployer 7 pipelines I maintain for legal-tech sister sites, Xdebug is absent from production releases entirely. Dev VMs get the package through Ansible provisioning playbooks. Composer 2.10 dev dependencies like PHPUnit may suggest Xdebug for coverage — run coverage on CI agents, not live traffic servers.

If you need remote debugging against a staging server — rare and risky — restrict by IP, use trigger mode, and disable afterward. For most teams, reproduce locally with synced database snapshots. Tools like the JSON formatter help inspect API payloads without attaching a debugger to staging.

Pair this discipline with Redis caching patterns and multi-server session config so performance tuning and debugging stay in separate lanes. When upgrades break dev tooling, Rector and Composer autoloader optimisation address code-level regressions Xdebug will not catch alone.

Need a full dev environment built correctly from the start? See custom software development or web development services for Laravel, Symfony, and WordPress projects with sane defaults. Read more on the PHP and Laravel blog, review shipped portfolio work, or learn about Kokil's background in full-stack delivery since 2010.

Key Takeaways

  • Set xdebug.mode=debug, port 9003, and start_with_request=trigger — then activate per request with a cookie or browser extension.
  • Install Xdebug for the same PHP binary your web SAPI uses; CLI-only installs fool almost everyone once.
  • Fix Docker and WSL2 by pointing xdebug.client_host at the host gateway IP, not container localhost.
  • Mirror absolute paths exactly in VS Code pathMappings and PhpStorm server settings.
  • Never load Xdebug on production — use trigger mode on staging and keep profiling sessions short.
  • Read /tmp/xdebug.log at log level 7 before you rewrite working application code.

People Also Ask

What port does PHP Xdebug 3 use for IDE debugging?

Xdebug 3 defaults to port 9003, not 9000. VS Code, PhpStorm, and firewall rules must listen on 9003. Older tutorials referencing 9000 are written for Xdebug 2 and will fail silently if only the IDE port is wrong.

Why does Xdebug 3 not connect to VS Code?

The top three causes are wrong client_host in Docker, mismatched path mappings, and debugging through a PHP binary that lacks the extension. Confirm with php -i | grep xdebug on the FPM pool version and tail xdebug.log during a triggered request.

Should xdebug.start_with_request be yes or trigger?

Use trigger on any machine others share or that resembles staging. Use yes only on isolated local VMs when you want every request to attempt debugging without a browser extension. Trigger mode avoids accidental performance drain.

Does Xdebug 3 work with PHP 8.5 and Laravel 13?

Yes. Install the Xdebug build compiled for your exact PHP version from PECL or your distro package manager. Laravel 13 requires PHP 8.3 minimum; Laravel 12 runs on PHP 8.2. Match the extension to whichever version serves the app — the IDE side stays the same.

Ship Faster With Working Debug Tools

PHP Xdebug 3 configuration for modern IDEs boils down to mode, host, port, and paths. Get those four right once per environment and breakpoints become boring again — which is exactly what you want when you are tracing payment callbacks or queue jobs on a deadline. If your team loses hours to broken debug setups after PHP upgrades or Docker migrations, get in touch for environment hardening, or explore ongoing support and maintenance so production and dev stacks stay aligned.

Frequently Asked Questions

Xdebug 3 defaults to port 9003, not Xdebug 2's 9000. VS Code, PhpStorm, and any firewall rules must listen on 9003 or the handshake fails silently.

Use trigger on shared or staging machines. Use yes only on isolated local VMs when you want every request to debug without a browser extension.

A comma-separated switch replacing Xdebug 2's separate enable flags. For IDE debugging, set xdebug.mode=debug. Add develop for better var_dump output.

Install the PHP Debug extension published by Xdebug. Create .vscode/launch.json at your project root with a Listen for Xdebug configuration on port 9003. Set pathMappings so the container absolute path, such as /var/www/html, maps to your local workspace folder — one character wrong and breakpoints stay grey and unverified. Match xdebug.idekey in your ini to VSCODE if you use the Xdebug Helper browser extension. Start listening in VS Code, trigger a session via cookie or query string, then hit your route. I see path mapping fail more often than port mismatches on Dockerized teams.

Open Settings, PHP, Debug and set the Xdebug port to 9003, not the legacy 9000. Enable Can accept external connections. Under PHP, Servers, add your vhost with path mappings identical to VS Code. Click the phone icon to listen. Match xdebug.idekey in your ini to PhpStorm's IDE key field when multiple developers share one machine. On a Laravel Livewire booking project I maintain, path mapping between Homestead and macOS was the only blocker after a PHP 8.4 upgrade once port and mode were correct.

The article names three top causes: wrong client_host inside Docker, mismatched IDE path mappings, and debugging through a PHP binary that does not have the extension loaded. Confirm the FPM pool version with php -i and grep xdebug, not CLI alone. Tail /tmp/xdebug.log at log level 7 during a triggered request and look for Connection refused or Time-out. Cross-check PHP-FPM pool settings if requests never reach PHP. I have spent afternoons on Ubuntu setups where the app ran fine but breakpoints never fired because ini landed in the wrong SAPI.

Inside a container, 127.0.0.1 points to the container itself, not your laptop. Set client_host to host.docker.internal with extra_hosts host-gateway in Docker Compose, or use your docker0 bridge IP on Linux without Docker Desktop. Sail documents SAIL_XDEBUG_MODE variables — follow those rather than hand-editing container ini when you use Sail. Pass XDEBUG_CONFIG with client_host, client_port 9003, and start_with_request=trigger. Map /var/www/html to your local clone in launch.json. Reload PHP-FPM after ini changes inside custom stacks, not only Apache.

When PHP runs inside WSL2 and your IDE runs on Windows, set xdebug.client_host to the Windows host IP from the nameserver line in /etc/resolv.conf. That IP can change after sleep or VPN connect, so a small shell alias to refresh it saves repeated frustration. Keep port 9003 aligned on both sides and verify path mappings between your WSL checkout and what VS Code opens. Enable xdebug.log temporarily and tail /tmp/xdebug.log while triggering a session to distinguish network timeouts from mapping issues.

Xdebug 3 replaces remote_enable with xdebug.mode=debug, changes the default IDE port from 9000 to 9003, renames remote_host to client_host and remote_autostart to start_with_request, while idekey stays the same. Profiler settings moved under xdebug.mode=profile as a separate mode you should not mix with daily debug work. Search your repo for 9000 in docker-compose, nginx configs, and old wiki pages and replace with 9003 everywhere. IDEs and firewall rules written for Xdebug 2 will fail silently if only one line still references the old port.

Match the Xdebug build to the exact PHP binary your web server uses — apt install php8.4-xdebug or pecl install xdebug when you need a specific patch across side-by-side versions. Place overrides in a dedicated drop-in such as /etc/php/8.4/mods-available/xdebug.ini rather than editing the main php.ini. Reload the SAPI you actually debug through: systemctl reload php8.4-fpm and apache2 if applicable. Confirm loaded values with php8.4 -i and grep xdebug.mode. CLI-only installs fool almost everyone once because Apache or PHP-FPM loads a different binary.

Never run xdebug.start_with_request=yes on shared staging servers — it adds overhead and opens unintended sessions. Use trigger mode instead. Activate per request with the Xdebug Helper browser extension for Chrome or Firefox, append ?XDEBUG_SESSION_START=VSCODE to a URL, set an XDEBUG_SESSION cookie, or export XDEBUG_SESSION=1 for one-off Artisan CLI commands. Background queue workers do not read browser cookies; pass the env var in your supervisor unit or run php artisan queue:listen from a terminal where you already exported it.

Install Xdebug for the PHP-FPM version serving the site — verify with php-fpm8.4 -i if needed. Laravel 13 requires PHP 8.3 minimum; Laravel 12 runs on PHP 8.2. Use trigger mode on shared Homestead or Sail instances. Map container paths to your local clone in launch.json. Place breakpoints in routes, controllers, or jobs, not compiled Blade views. Run php artisan config:clear only when debugging config cache issues; Xdebug itself does not need it. Reach for Xdebug when state mutates across middleware or queue serialization behaves oddly — not for every typo Laravel Telescope already shows.

Symfony 8.1 requires PHP 8.4.1 minimum — align Xdebug with that exact build. Symfony CLI can proxy requests with its own PHP binary; confirm which one serves symfony server:start using symfony php -i and grep xdebug. Path mappings must reach public/index.php correctly through Symfony's front controller. PHPUnit and Pest tests respect Xdebug when triggered, but disable Xdebug entirely in CI pipelines for speed. Use Symfony Profiler or PHPStan for many production-like issues; attach the debugger when middleware or env-specific behaviour needs step-through inspection.

Xdebug must not load on production web nodes — it adds measurable latency and exposes internal structure. Comment out zend_extension=xdebug.so or remove the package entirely on production. On Deployer 7 pipelines I maintain for legal-tech sister sites, Xdebug is absent from production releases; dev VMs get the package through provisioning. Use trigger mode only on isolated staging if you must, restrict by IP, and disable afterward. Composer dev dependencies like PHPUnit may suggest Xdebug for coverage — run coverage on CI agents, not live traffic servers. Reproduce staging bugs locally with synced database snapshots instead.

Work through path maps, PHP binary, log file, then network — in that order. Read /tmp/xdebug.log at xdebug.log_level=7 before rewriting application code. Confirm php8.4 -i shows xdebug.mode=debug and that you reloaded php8.4-fpm, not only CLI. Verify the IDE listens on 9003 and pathMappings mirror absolute container paths exactly. In Docker, client_host must aim at the host gateway, not container localhost. Firewalls on port 9003 are rare locally but common on remote dev VMs. Fix ini placement first; on real Ubuntu PHP server setups the app often runs fine while breakpoints never fire because overrides landed in the wrong ini file.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: