|
| 1 | +--- |
| 2 | +title: "Xdebug in DDEV: Understanding, Debugging, and Troubleshooting Step Debugging" |
| 3 | +pubDate: 2026-02-17 |
| 4 | +summary: Understanding how Xdebug step debugging works, how it's integrated in DDEV, and how to diagnose and fix common connectivity issues. |
| 5 | +author: Randy Fay |
| 6 | +featureImage: |
| 7 | + src: /img/blog/2026/02/xdebug-debugging.png |
| 8 | + alt: Illustration showing how Xdebug connects from PHP container to IDE debugger |
| 9 | +categories: |
| 10 | + - Training |
| 11 | + - TechNotes |
| 12 | +--- |
| 13 | + |
| 14 | +For most people, Xdebug step debugging in DDEV just works: `ddev xdebug on`, set a breakpoint, start your IDE's debug listener, and go. DDEV handles all the Docker networking automatically. If you're having trouble, run `ddev utility xdebug-diagnose` and `ddev utility xdebug-diagnose --interactive` — they check your configuration and connectivity and tells you exactly what to fix. |
| 15 | + |
| 16 | +This post explains how the pieces fit together and what to do if things do go wrong. |
| 17 | + |
| 18 | +## The Quick Version |
| 19 | + |
| 20 | +1. `ddev xdebug on` |
| 21 | +2. Start listening in your IDE (PhpStorm: click the phone icon; VS Code: press <kbd>F5</kbd>) |
| 22 | +3. Set a breakpoint in your entry point (`index.php` or `web/index.php`) |
| 23 | +4. Visit your site |
| 24 | + |
| 25 | +If it doesn't work: |
| 26 | + |
| 27 | +```bash |
| 28 | +ddev utility xdebug-diagnose |
| 29 | +``` |
| 30 | + |
| 31 | +Or for guided, step-by-step troubleshooting: |
| 32 | + |
| 33 | +```bash |
| 34 | +ddev utility xdebug-diagnose --interactive |
| 35 | +``` |
| 36 | + |
| 37 | +The diagnostic checks port 9003 listener status, `host.docker.internal` resolution, WSL2 configuration, `xdebug_ide_location`, network connectivity, and whether Xdebug is loaded. It gives actionable fix recommendations. |
| 38 | + |
| 39 | +## How Xdebug Works |
| 40 | + |
| 41 | +[Xdebug](https://xdebug.org/) lets you set breakpoints, step through code, and inspect variables — interactive debugging instead of `var_dump()`. |
| 42 | + |
| 43 | +The connection model is a reverse connection: your IDE listens on port 9003 (it's the TCP server), and PHP with Xdebug initiates the connection (it's the TCP client). Your IDE must be listening _before_ PHP tries to connect. |
| 44 | + |
| 45 | +:::note |
| 46 | +The [Xdebug documentation](https://xdebug.org/docs/step_debug) uses the opposite terminology, calling the IDE the "client." We use standard TCP terminology here. |
| 47 | +::: |
| 48 | + |
| 49 | +## How DDEV Makes It Work |
| 50 | + |
| 51 | +DDEV configures Xdebug to connect to `host.docker.internal:9003`. This special hostname resolves to the host machine's IP address from inside the container, so PHP can reach your IDE across the Docker boundary. |
| 52 | + |
| 53 | +The tricky part is that `host.docker.internal` works differently across platforms. DDEV handles this automatically: |
| 54 | + |
| 55 | +- **macOS/Windows**: Docker Desktop and Colima provide `host.docker.internal` natively |
| 56 | +- **Linux**: DDEV uses the docker-compose host gateway feature |
| 57 | +- **WSL2**: DDEV determines the correct IP based on your configuration |
| 58 | + |
| 59 | +You can verify the resolution with: |
| 60 | + |
| 61 | +```bash |
| 62 | +ddev exec getent hosts host.docker.internal |
| 63 | +``` |
| 64 | + |
| 65 | +### DDEV Xdebug Commands |
| 66 | + |
| 67 | +- `ddev xdebug on` / `off` / `toggle` — Enable, disable, or toggle Xdebug |
| 68 | +- `ddev xdebug status` — Check if Xdebug is enabled |
| 69 | +- `ddev xdebug info` — Show configuration and connection details |
| 70 | + |
| 71 | +## IDE Setup |
| 72 | + |
| 73 | +### PhpStorm |
| 74 | + |
| 75 | +Zero-configuration debugging works out of the box: |
| 76 | + |
| 77 | +1. Run → Start Listening for PHP Debug Connections |
| 78 | +2. Set a breakpoint |
| 79 | +3. Visit your site |
| 80 | + |
| 81 | +PhpStorm auto-detects the server and path mappings. If mappings are wrong, check Settings → PHP → Servers and verify `/var/www/html` maps to your project root. |
| 82 | + |
| 83 | +The [PhpStorm DDEV Integration](https://plugins.jetbrains.com/plugin/18813-ddev-integration) plugin handles this automatically. |
| 84 | + |
| 85 | +### VS Code |
| 86 | + |
| 87 | +Install the [PHP Debug extension](https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug) and create `.vscode/launch.json`: |
| 88 | + |
| 89 | +```json |
| 90 | +{ |
| 91 | + "version": "0.2.0", |
| 92 | + "configurations": [ |
| 93 | + { |
| 94 | + "name": "Listen for Xdebug", |
| 95 | + "type": "php", |
| 96 | + "request": "launch", |
| 97 | + "port": 9003, |
| 98 | + "hostname": "0.0.0.0", |
| 99 | + "pathMappings": { |
| 100 | + "/var/www/html": "${workspaceFolder}" |
| 101 | + } |
| 102 | + } |
| 103 | + ] |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +The [VS Code DDEV Manager](https://marketplace.visualstudio.com/items?itemName=biati.ddev-manager) extension can set this up for you. |
| 108 | + |
| 109 | +**WSL2 + VS Code with WSL extension**: Install the PHP Debug extension in WSL, not Windows. |
| 110 | + |
| 111 | +## Common Issues |
| 112 | + |
| 113 | +Most problems fall into a few categories. The `ddev utility xdebug-diagnose` tool checks for all of these automatically. |
| 114 | + |
| 115 | +**Breakpoint in code that doesn't execute**: The #1 issue. Start with a breakpoint in your entry point (`index.php`) to confirm Xdebug works, then move to the code you actually want to debug. |
| 116 | + |
| 117 | +**IDE not listening**: Make sure you've started the debug listener. PhpStorm: click the phone icon. VS Code: press F5. |
| 118 | + |
| 119 | +**Incorrect path mappings**: Xdebug reports container paths (`/var/www/html`), and your IDE needs to map them to your local project. PhpStorm usually auto-detects this; VS Code needs the `pathMappings` in `launch.json`. |
| 120 | + |
| 121 | +**Firewall blocking the connection**: Especially common on WSL2, where Windows Defender Firewall blocks connections from the Docker container. Quick test: temporarily disable your firewall. If debugging works, add a firewall rule for port 9003. |
| 122 | + |
| 123 | +## WSL2 Notes |
| 124 | + |
| 125 | +WSL2 adds networking complexity. The most common problems: |
| 126 | + |
| 127 | +- **Windows Defender Firewall** blocks connections from WSL2 to Windows. Temporarily disable it to test; if debugging works, add a rule for port 9003. |
| 128 | +- **Mirrored mode** requires `hostAddressLoopback=true` in `C:\Users\<username>\.wslconfig`: |
| 129 | + |
| 130 | + ```ini |
| 131 | + [experimental] |
| 132 | + hostAddressLoopback=true |
| 133 | + ``` |
| 134 | + |
| 135 | + Then `wsl --shutdown` to apply. |
| 136 | + |
| 137 | +- **IDE in WSL2** (VS Code + WSL extension): Set `ddev config global --xdebug-ide-location=wsl2` |
| 138 | + |
| 139 | +## Special Cases |
| 140 | + |
| 141 | +**Container-based IDEs** (VS Code Remote Containers, JetBrains Gateway): |
| 142 | + |
| 143 | +```bash |
| 144 | +ddev config global --xdebug-ide-location=container |
| 145 | +``` |
| 146 | + |
| 147 | +**Command-line debugging**: Works the same way — `ddev xdebug on`, start your IDE listener, then `ddev exec php myscript.php`. Works for Drush, WP-CLI, Artisan, and any PHP executed in the container. |
| 148 | + |
| 149 | +**Debugging Composer**: Composer disables Xdebug by default. Override with: |
| 150 | + |
| 151 | +```bash |
| 152 | +ddev exec COMPOSER_ALLOW_XDEBUG=1 composer install |
| 153 | +``` |
| 154 | + |
| 155 | +**Custom port**: Create `.ddev/php/xdebug_client_port.ini` with `xdebug.client_port=9000` (rarely needed). |
| 156 | + |
| 157 | +**Debugging host.docker.internal resolution**: Run `DDEV_DEBUG=true ddev start` to see how DDEV determines the IP. |
| 158 | + |
| 159 | +## Advanced Features |
| 160 | + |
| 161 | +**xdebugctl**: DDEV includes the [`xdebugctl` utility](https://github.com/xdebug/xdebugctl) for dynamically querying and modifying Xdebug settings, switching modes (debug, profile, trace), and more. Run `ddev exec xdebugctl --help`. See the [xdebugctl documentation](https://xdebug.org/docs/xdebugctl). |
| 162 | + |
| 163 | +**Xdebug map feature**: Recent Xdebug versions can remap file paths during debugging, useful when container paths don't match local paths in complex ways. This complements IDE path mappings. |
| 164 | + |
| 165 | +**Performance**: Xdebug adds overhead. Use `ddev xdebug off` or `ddev xdebug toggle` when you're not actively debugging. |
| 166 | + |
| 167 | +## More Information |
| 168 | + |
| 169 | +- [DDEV Step Debugging Documentation](https://docs.ddev.com/en/stable/users/debugging-profiling/step-debugging/) |
| 170 | +- [Xdebug Documentation](https://xdebug.org/docs/) |
| 171 | +- [Xdebug Step Debugging Guide](https://xdebug.org/docs/step_debug) |
| 172 | + |
| 173 | +Claude Code was used to create an initial draft for this blog, and for subsequent reviews. |
0 commit comments