Disclosure: I am the creator of Stratos, a native macOS local PHP/Laravel stack.

If you have ever spent 45 minutes trying to figure out why an Xdebug breakpoint silently never hits inside Docker Desktop for Mac, the culprit is almost never your PHP code. It is the translation layer between the Linux container and macOS.

Here is why step-debugging breaks so often in Docker—and how running native Apple Silicon/Universal PHP-FPM (8.1–8.5) eliminates host.docker.internal and pathMappings friction completely.

Why Docker Xdebug Breaks on macOS

When PHP runs inside a Linux container, Xdebug initiates an outbound TCP connection back to your IDE on port 9003. That introduces three failure points on every restart:

  1. host.docker.internal routing & firewalls: Depending on Docker Desktop / Colima / OrbStack networking modes and macOS firewall rules, xdebug.client_host=host.docker.internal can drop packets or resolve inconsistently.
  2. Container-to-Mac pathMappings drift: PHP inside Docker thinks your controller lives at /var/www/html/app/Http/Controllers/HomeController.php, while Cursor, VS Code, or PhpStorm sees /Users/you/Sites/my-app/app/Http/Controllers/HomeController.php. One mismatched trailing slash or symlink causes every breakpoint to be ignored.
  3. Always-on overhead: Leaving xdebug.start_with_request=yes enabled inside shared container images slows down every request—even when your IDE listener is off.

Why Native macOS PHP-FPM Maps 1:1 on 127.0.0.1:9003

When PHP-FPM runs directly on macOS (without a Linux VM), PHP executes against the exact same APFS paths your editor already has open. There is no container filesystem boundary and no NAT translation:

  • Direct loopback: PHP connects straight to 127.0.0.1:9003.
  • 1:1 file paths: No pathMappings block is required in .vscode/launch.json, and PhpStorm needs no server path mapping table.
  • Per-version isolation: Each PHP FPM pool (8.1, 8.2, 8.3, 8.4, 8.5) loads its own extensions cleanly.

Exact Xdebug 3 php.ini Configuration

In Stratos, toggling Stratos → Settings → PHP Extensions → Xdebug applies these Xdebug 3 directives to the selected PHP version and restarts its FPM pool in one click:

zend_extension=xdebug.so xdebug.mode=develop,debug xdebug.start_with_request=yes xdebug.client_port=9003 xdebug.client_host=127.0.0.1 xdebug.idekey=PHPSTORM 

You can keep Xdebug enabled only while actively debugging and toggle it back off in one click when benchmarking or running tests.

Step 1: Cursor / VS Code .vscode/launch.json

Install the standard PHP Debug extension (xdebug.php-debug) in Cursor or VS Code, then add .vscode/launch.json at your project root:

{ "version": "0.2.0", "configurations": [ { "name": "Listen for Xdebug (Stratos Native)", "type": "php", "request": "launch", "port": 9003, "hostname": "127.0.0.1" } ] } 

Notice there is no pathMappings key at all. Open any Laravel route or controller, set a breakpoint, press F5 (or Run → Start Debugging), and reload https://your-site.test.

Step 2: PhpStorm Zero-Mapping Setup

  1. Open Settings → PHP → Debug.
  2. Confirm Debug port includes 9003 and Can accept external connections is checked.
  3. Click the Start Listening for PHP Debug Connections (Telephone icon) in the top-right toolbar.
  4. Trigger your .test URL in the browser or run php artisan from the terminal.

Because PHP runs natively on macOS, PhpStorm matches the incoming file path directly without mapping /var/www/html.

Troubleshooting Port 9003 Conflicts & Profiling With SPX

If breakpoints still fail to connect, verify nothing else (such as an older Xdebug daemon, stale SSH tunnel, or another IDE window) is holding port 9003:

lsof -i :9003 

Make sure you do not have both Cursor/VS Code and PhpStorm listening on 9003 simultaneously.

Use SPX Profiler for Flame Graphs Without Step-Debugging Overhead

Step-debugging is great for inspecting state line-by-line, but Xdebug's debug mode adds execution overhead when you actually want performance timings. For bottleneck hunting in Laravel requests, pair Xdebug with SPX Profiler:

  1. Toggle SPX on in Stratos Settings (while keeping Xdebug off when profiling).
  2. Append the query parameter to any local .test URL:
https://my-app.test/?SPX_KEY=dev&SPX_UI_URI=/ 

You get interactive call flame graphs and memory/time attribution per function call without idle Xdebug pauses.

Free Forever Core Stack

If you are moving off Docker Desktop on macOS, multi-PHP 8.1–8.5 serving, trusted .test HTTPS, 1-click Xdebug/SPX toggles, Site Doctor port diagnostics, and the Free read-only MCP bridge (http://127.0.0.1:9921/mcp) are all Free Forever ($0) in Stratos—no subscription required for local hosting.