stabil zum Laufen bringen
Xdebug mit Docker ist kein One-Click-Setup: host.docker.internal, Port 9003, Path Mappings, der korrekte xdebug.mode und PHPStorm-Server-Konfiguration müssen zusammenpassen. Dieser Artikel bringt alle Teile systematisch in die richtige Position – für stabile Breakpoints in Magento 2 und anderen PHP-Projekten.
Inhaltsverzeichnis
- 1. Wie Xdebug mit Docker und PHPStorm zusammenarbeitet
- 2. xdebug.ini: die vollständige Konfiguration für Docker
- 3. Linux-spezifisches Problem: host-gateway konfigurieren
- 4. PHPStorm Server-Konfiguration und Path Mappings
- 5. Debug-Listening aktivieren und erste Breakpoints testen
- 6. CLI-Debugging: Magento-Befehle debuggen
- 7. Xdebug und Performance: nur aktivieren wenn nötig
- 8. Häufige Fehler und ihre Lösungen im Vergleich
- 9. Zusammenfassung
- 10. FAQ
1. Wie Xdebug mit Docker und PHPStorm zusammenarbeitet
Um Xdebug mit Docker und PHPStorm stabil zu konfigurieren, muss man die Kommunikationsarchitektur verstehen. Xdebug ist eine PHP-Extension, die im PHP-Container läuft. Wenn PHP einen Breakpoint erreicht, initiiert Xdebug eine TCP-Verbindung zurück zur IDE – nicht umgekehrt. Das bedeutet: der Container muss den Entwicklerrechner (Host) erreichen können. PHPStorm hört auf einem Port (standardmäßig 9003 bei Xdebug 3) auf eingehende Xdebug-Verbindungen.
Diese Richtung der Verbindung ist der Kern des Docker-Problems. Im Container ist der Host nicht automatisch erreichbar. Unter macOS und Windows stellt Docker Desktop host.docker.internal bereit – ein DNS-Name, der auf die Host-IP zeigt. Unter Linux gibt es diesen Namen standardmäßig nicht. Dort muss er explizit über extra_hosts in der Compose-Konfiguration definiert werden: host.docker.internal:host-gateway. host-gateway ist ein Docker-interner Alias für die Gateway-IP des Docker-Bridge-Netzwerks, die typischerweise die IP des Hosts im Container-Netzwerk ist.
2. xdebug.ini: die vollständige Konfiguration für Docker
Die Xdebug-Konfiguration wird in einer separaten xdebug.ini-Datei definiert, die als PHP-Extension-Konfiguration eingebunden wird. Die wichtigsten Einstellungen für Xdebug 3 in Docker: xdebug.mode=debug aktiviert den Step-Debugger. xdebug.start_with_request=yes startet Xdebug bei jeder PHP-Anfrage automatisch. xdebug.client_host=host.docker.internal gibt die Adresse an, an die Xdebug die Verbindung aufbaut. xdebug.client_port=9003 ist der Standard-Port für Xdebug 3 (Xdebug 2 nutzte 9000).
Die Einstellung xdebug.start_with_request=yes bedeutet, dass Xdebug bei jeder Anfrage startet – auch wenn PHPStorm nicht auf eine Verbindung wartet. Das führt zu einem kurzen Timeout bei jeder Anfrage, wenn PHPStorm nicht hört. Eine Alternative ist xdebug.start_with_request=trigger: Xdebug startet nur, wenn ein spezieller Cookie, Header oder GET-Parameter vorhanden ist. Das ist besser für die Performance im Alltag, erfordert aber Browser-Extensions oder manuelle Header für den Trigger.
<?php
// docker/phpfpm/xdebug.ini — Xdebug 3 for Docker + PHPStorm
/*
[xdebug]
; Core settings
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
xdebug.client_host=host.docker.internal
xdebug.idekey=PHPSTORM
; Logging (disable in production, enable for troubleshooting)
xdebug.log_level=0
; xdebug.log=/tmp/xdebug.log
; Magento-specific: increase nesting level
xdebug.max_nesting_level=512
; Performance: disable coverage when not needed
; Set to 'debug,coverage' only when running PHPUnit with coverage
; For trigger-mode (better performance, needs browser extension):
; xdebug.start_with_request=trigger
; xdebug.trigger_value=PHPSTORM
*/
// Verify Xdebug is loaded in container:
// bin/cli php -v | grep Xdebug
// Expected: Xdebug v3.x.x, Copyright (c) ...
// Check which settings are active:
// bin/cli php -r "var_dump(xdebug_info());"
3. Linux-spezifisches Problem: host-gateway konfigurieren
Das Linux-spezifische Problem mit Xdebug und Docker ist das häufigste Hindernis für PHP-Entwickler, die von macOS auf Linux wechseln oder Linux als Entwicklungsplattform nutzen. Das Mark-Shust-Docker-Setup hat für diesen Fall die Datei compose.dev-linux.yaml, die spezifische Linux-Konfigurationen enthält. Alternativ kann man das extra_hosts-Flag direkt in die Haupt-compose.yaml eintragen, wenn nur Linux als Entwicklungsplattform verwendet wird.
Nach dem Hinzufügen von extra_hosts: - "host.docker.internal:host-gateway" zum phpfpm-Service muss der Container neu gestartet werden. Zur Verifikation: Im Container ping host.docker.internal oder curl -v telnet://host.docker.internal:9003 ausführen, wenn PHPStorm gerade auf Verbindungen wartet. Eine Antwort (auch eine Verbindungsablehnung, solange PHPStorm nicht hört) zeigt, dass die DNS-Auflösung funktioniert. Bleibt der Ping aus, ist extra_hosts nicht korrekt konfiguriert.
<?php
// compose.dev-linux.yaml — Linux-specific Xdebug fix
/*
services:
phpfpm:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
PHP_IDE_CONFIG: "serverName=mironsoft-docker"
XDEBUG_CONFIG: "client_host=host.docker.internal"
# Use with: docker compose -f compose.yaml -f compose.dev-linux.yaml up -d
# Alternative: add directly to compose.yaml if Linux-only setup
# Verification steps:
# 1. Start containers: bin/start (or docker compose up -d)
# 2. Enter container: bin/bash
# 3. Test connectivity: ping -c 1 host.docker.internal
# 4. Should resolve to Docker bridge IP (172.17.0.1 or similar)
# 5. Enable PHPStorm debug listening (phone icon in toolbar)
# 6. Test: nc -zv host.docker.internal 9003
# Should show: Connection to host.docker.internal 9003 port [tcp/*] succeeded
*/
4. PHPStorm Server-Konfiguration und Path Mappings
Auf der PHPStorm-Seite sind zwei Konfigurationsbereiche für Xdebug relevant: die Debug-Einstellungen und die Server-Konfiguration. Die Debug-Einstellungen findet man unter Settings → PHP → Debug: Xdebug-Port auf 9003 setzen, "Can accept external connections" aktivieren. Die Server-Konfiguration unter Settings → PHP → Servers definiert, wie PHPStorm Dateipfade aus dem Xdebug-Protokoll auf lokale Dateien mappt.
Die Server-Konfiguration: Settings → PHP → Servers → +. Name: mironsoft-docker (muss dem Wert in PHP_IDE_CONFIG=serverName=mironsoft-docker entsprechen). Host: localhost. Port: 80. Debugger: Xdebug. "Use path mappings" aktivieren. Dann das lokale Projektverzeichnis /home/mir/development/mironsoft/src dem Container-Pfad /var/www/html zuordnen. Dieser Mapping-Schritt ist entscheidend: ohne ihn öffnet PHPStorm beim Breakpoint-Halt keine lokale Datei, sondern zeigt einen "Cannot open file"-Fehler.
<?php
// PHPStorm Server Configuration Summary
// Settings → PHP → Servers
/*
Server Settings:
- Name: mironsoft-docker
(matches PHP_IDE_CONFIG="serverName=mironsoft-docker" in container)
- Host: localhost
- Port: 80 (or 443 for HTTPS)
- Debugger: Xdebug
Path Mappings (Use path mappings ✓):
Local Path → Absolute path on server
/home/mir/development/mironsoft/src → /var/www/html
Settings → PHP → Debug:
- Debug port: 9003
- ✓ Can accept external connections
- ✓ Force break at first line when no path mapping specified
- ✓ Force break at first line when a script is outside the project
// Environment variable in container (compose.yaml):
// environment:
// PHP_IDE_CONFIG: "serverName=mironsoft-docker"
// PHPStorm uses PHP_IDE_CONFIG to auto-select the server
// when a new Xdebug session arrives
*/
5. Debug-Listening aktivieren und erste Breakpoints testen
Nachdem alle Einstellungen konfiguriert sind, ist der Workflow für einen Debugging-Session: PHPStorm in den "Listen"-Modus versetzen (Telefon-Icon in der oberen Toolbar oder Run → Start Listening for PHP Debug Connections). Dann im Browser die Magento-Seite aufrufen, die debuggt werden soll. Wenn xdebug.start_with_request=yes aktiv ist, verbindet sich Xdebug automatisch. Wenn der Trigger-Modus aktiv ist, muss der Xdebug-Cookie oder Header gesetzt sein (via Browser-Extension wie "Xdebug Helper").
Beim ersten erfolgreichen Verbindungsaufbau zeigt PHPStorm entweder den Incoming Connection Dialog (wenn PHPStorm nicht weiß, welchen Server es nutzen soll) oder hält direkt am ersten Breakpoint. Falls der Dialog erscheint, wird der Server dort ausgewählt und Path Mappings können direkt bestätigt werden. Ein häufiger erster Test: einen Breakpoint in index.php setzen (Magento-Einstiegspunkt) und eine beliebige Frontend-Seite aufrufen. Hält PHPStorm dort, funktioniert das gesamte Setup korrekt.
6. CLI-Debugging: Magento-Befehle debuggen
Xdebug funktioniert nicht nur für Web-Requests, sondern auch für CLI-Befehle. Das ist besonders nützlich für Magento, weil viele Kernprozesse als CLI-Commands laufen: setup:upgrade, indexer:reindex, Daten-Migration und eigene CLI-Commands. Um einen CLI-Befehl zu debuggen, muss PHP_IDE_CONFIG in der Shell-Umgebung gesetzt sein und Xdebug muss für CLI aktiviert sein.
Im Mark-Shust-Setup gibt es dafür das Script bin/debug-cli enable, das die nötigen PHP-Konfigurationen für CLI-Debugging aktiviert. Alternativ direkt die XDEBUG_SESSION-Umgebungsvariable setzen: XDEBUG_SESSION=PHPSTORM bin/magento cache:flush. PHPStorm muss im Listen-Modus sein. Der CLI-Prozess im Container verbindet sich dann über host.docker.internal:9003 mit PHPStorm. Path Mappings müssen korrekt sein – identisch wie für Web-Requests.
7. Xdebug und Performance: nur aktivieren wenn nötig
Xdebug hat einen signifikanten Performance-Overhead. Mit aktiviertem Xdebug (auch ohne aktive Debugging-Session) läuft PHP langsamer – der Overhead durch das Instrumentieren aller Funktionsaufrufe und das Prüfen auf Debug-Sessions beträgt je nach Anwendung 10–50% mehr Ausführungszeit. Für Magento 2, das ohnehin ressourcenintensiv ist, ist das besonders spürbar: Seiten laden merklich langsamer, CLI-Befehle dauern länger.
Die empfohlene Strategie: Xdebug nicht permanent aktiviert lassen. Im Mark-Shust-Setup gibt es bin/xdebug enable und bin/xdebug disable – diese Skripte aktivieren oder deaktivieren Xdebug im Container durch Änderung der PHP-Konfiguration ohne Container-Neustart. Alternativ den Trigger-Modus nutzen: xdebug.start_with_request=trigger. Dann läuft PHP ohne Overhead, bis ein expliziter Trigger-Cookie oder -Header gesetzt wird. Die Browser-Extension "Xdebug Helper" macht das Setzen des Triggers mit einem Klick möglich.
8. Häufige Fehler und ihre Lösungen im Vergleich
| Symptom | Ursache | Lösung | Verifikation |
|---|---|---|---|
| PHPStorm hört, aber Breakpoint wird nie erreicht | host.docker.internal nicht auflösbar (Linux) | extra_hosts: host.docker.internal:host-gateway | Im Container: ping host.docker.internal |
| PHPStorm öffnet bei Breakpoint keine Datei | Path Mappings fehlen oder falsch | Settings → PHP → Servers → Path Mappings | Local: /src → Container: /var/www/html |
| Xdebug verbindet, aber falscher Server | PHP_IDE_CONFIG serverName fehlt | PHP_IDE_CONFIG=serverName=mironsoft-docker | Incoming-Connection-Dialog zeigt Server-Auswahl |
| Port 9003 belegt, Verbindung schlägt fehl | Anderer Prozess nutzt Port 9003 | ss -tulpn | grep 9003, Prozess beenden | PHPStorm: Settings → PHP → Debug → Port |
| Xdebug aktiv, aber keine Verbindung | PHPStorm nicht im Listen-Modus | Run → Start Listening for PHP Debug Connections | Telefon-Icon in Toolbar ist grün/aktiv |
9. Zusammenfassung
Xdebug mit Docker und PHPStorm stabil zu konfigurieren erfordert das korrekte Zusammenspiel von vier Komponenten: Xdebug-INI im Container (mode, client_host, client_port), Docker-Netzwerk-Konfiguration (host-gateway unter Linux), PHPStorm-Server-Konfiguration mit Path Mappings und dem aktivierten Debug-Listening-Modus. Wenn alle vier Teile korrekt sind, funktioniert das Debugging zuverlässig – für Web-Requests, GraphQL-Requests und CLI-Befehle gleichermaßen.
Xdebug mit Docker und PHPStorm — Das Wichtigste auf einen Blick
xdebug.ini
mode=debug, start_with_request=yes, client_host=host.docker.internal, client_port=9003, max_nesting_level=512.
Linux: host-gateway
extra_hosts: host.docker.internal:host-gateway im phpfpm-Service. Container neu starten. Verifikation: ping host.docker.internal im Container.
PHPStorm Server
Settings → PHP → Servers. Name = serverName in PHP_IDE_CONFIG. Path Mapping: lokal /src → Container /var/www/html.
Performance
Xdebug nur aktivieren wenn nötig. bin/xdebug enable|disable oder Trigger-Modus. Browser-Extension für Trigger-Cookie.