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:
-
host.docker.internalrouting & firewalls: Depending on Docker Desktop / Colima / OrbStack networking modes and macOS firewall rules,xdebug.client_host=host.docker.internalcan drop packets or resolve inconsistently. -
Container-to-Mac
pathMappingsdrift: 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. -
Always-on overhead: Leaving
xdebug.start_with_request=yesenabled 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
pathMappingsblock 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
- Open Settings → PHP → Debug.
- Confirm Debug port includes
9003and Can accept external connections is checked. - Click the Start Listening for PHP Debug Connections (Telephone icon) in the top-right toolbar.
- Trigger your
.testURL in the browser or runphp artisanfrom 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:
- Toggle SPX on in Stratos Settings (while keeping Xdebug off when profiling).
- Append the query parameter to any local
.testURL:
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.
- Full Xdebug guide: Setup Xdebug in Cursor, VS Code & PhpStorm on macOS (Port 9003)
- Stack & cost comparison: Compare Stratos vs Herd Pro, Docker Desktop, DDEV, Valet & LocalWP
Top comments (0)