Path-Mapping, IDE-Integration und Performance-Modi
Xdebug in Docker-Containern funktioniert nur dann zuverlässig, wenn man die drei häufigsten Fallstricke kennt: falsche Host-Erkennung, fehlendes Path-Mapping und Xdebug-Overhead im normalen Betrieb. Wer diese Punkte einmal sauber löst, debuggt danach in Docker genauso komfortabel wie lokal.
Inhaltsverzeichnis
- 1. Warum Xdebug in Docker oft nicht funktioniert
- 2. Wie Xdebug und Docker kommunizieren
- 3. Xdebug im Dockerfile installieren
- 4. xdebug.ini korrekt konfigurieren
- 5. Host-IP-Erkennung auf macOS, Linux und WSL2
- 6. Path-Mapping: der häufigste Fehler erklärt
- 7. PhpStorm-Integration Schritt für Schritt
- 8. VS Code mit PHP Debug Extension
- 9. Xdebug-Modi im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Xdebug in Docker oft nicht funktioniert
Xdebug in Docker ist konzeptuell anders als lokales Debugging: Der PHP-Prozess läuft im Container, die IDE läuft auf dem Host. Xdebug initiiert die Verbindung von sich aus – es verbindet sich aktiv zur IDE, nicht umgekehrt. Das bedeutet, Xdebug muss die IP-Adresse des IDE-Hosts kennen und dieser Host muss auf dem konfigurierten Port (Standard 9003) erreichbar sein. Auf einem lokalen System ohne Netzwerk-Trennung ist das trivial; in Docker mit eigenen virtuellen Netzwerken wird es zur Herausforderung.
Die drei häufigsten Fehler beim Einrichten von Xdebug in Docker sind: erstens eine falsch konfigurierte oder nicht auflösbare Host-IP, zweitens ein fehlendes oder falsches Path-Mapping zwischen den Pfaden im Container und den lokalen Pfaden, und drittens ein aktiviertes Xdebug im normalen Betrieb, das jede Request durch Instrumentation erheblich verlangsamt. Wer alle drei Punkte einmal korrekt konfiguriert hat, erlebt danach zuverlässiges Debugging ohne Überraschungen – egal ob mit PhpStorm, VS Code oder dem CLI-Debugger.
2. Wie Xdebug und Docker kommunizieren
Xdebug 3 nutzt das DAP-Protokoll (Debug Adapter Protocol) über eine TCP-Verbindung. Wenn PHP eine Datei ausführt und Xdebug aktiv ist, initiiert die Extension eine TCP-Verbindung zur konfigurierten Client-Adresse (früher als Remote-Host bezeichnet) auf dem konfigurierten Port. Auf dem anderen Ende dieser Verbindung wartet die IDE – entweder permanent oder nach dem Klick auf den "Listen for Xdebug Connections"-Button. Sobald die Verbindung steht, schickt Xdebug Debuginformationen und empfängt Steuerbefehle (weiter, step over, breakpoint setzen).
In einer Xdebug-Docker-Umgebung muss also der Container die IDE-Adresse auflösen können. Docker-Container sind in einem eigenen virtuellen Netzwerk und erreichen den Host über die Gateway-IP des Docker-Netzwerks oder über einen speziellen Hostnamen. Die Firewall des Hosts muss den Xdebug-Port von der Container-IP akzeptieren. Unter Linux muss ggf. eine iptables-Regel hinzugefügt werden; unter macOS und Docker Desktop ist die Gateway-IP automatisch auf dem Host verfügbar.
# Dockerfile.dev — Install Xdebug for development builds only
FROM php:8.4-fpm
# Install Xdebug via pecl (do NOT use apt-get xdebug packages — outdated)
RUN pecl install xdebug-3.4.0 \
&& docker-php-ext-enable xdebug
# Create a separate xdebug.ini — do NOT mix with php.ini
# The file must be loaded by PHP's additional ini scan directory
COPY docker/php/xdebug.ini /usr/local/etc/php/conf.d/99-xdebug.ini
# Verify installation
RUN php -v | grep -i xdebug
# Note: production Dockerfile should NOT install Xdebug
# Use multi-stage builds to keep production images lean
3. Xdebug im Dockerfile installieren
Xdebug in Docker wird am saubersten über pecl install xdebug im Dockerfile installiert. Das offizielle PHP-Docker-Image enthält docker-php-ext-enable, das die Extension-Konfiguration automatisch an der richtigen Stelle ablegt. Die Installation sollte in einem dedizierten Development-Dockerfile stattfinden, nicht im Production-Image. Multi-Stage-Builds ermöglichen es, dasselbe Basis-Image zu verwenden und Xdebug nur in der Development-Stage hinzuzufügen, während die Production-Stage schlank bleibt.
Ein häufiger Fehler ist das Mischen der Xdebug-Konfiguration mit der allgemeinen PHP-Konfiguration in php.ini. Besser ist eine dedizierte Datei 99-xdebug.ini, die nur Xdebug-Direktiven enthält. Die Zahl 99 im Präfix stellt sicher, dass die Datei nach allen anderen ini-Dateien geladen wird und Einstellungen überschreiben kann. Diese Datei kann über einen Bind-Mount oder eine Umgebungsvariable zwischen Develop- und Debug-Modus umgeschaltet werden, ohne das Image neu zu bauen.
4. xdebug.ini korrekt konfigurieren
Xdebug 3 hat das Konfigurationssystem gegenüber Xdebug 2 vollständig überarbeitet. Der wichtigste Unterschied: Es gibt keine separaten remote_enable- und remote_connect_back-Direktiven mehr. Stattdessen steuert der xdebug.mode-Parameter, welche Features aktiv sind. Der Modus debug aktiviert das Step-Debugging, coverage aktiviert Code-Coverage-Messung, profile aktiviert den Profiler. Mehrere Modi können kommagetrennt kombiniert werden.
Die xdebug.client_host-Direktive ersetzt das alte remote_host und gibt die IP-Adresse oder den Hostnamen an, unter dem die IDE erreichbar ist. Für Xdebug in Docker auf macOS ist das der spezielle Hostname host.docker.internal, der automatisch auf die Host-IP zeigt. Auf Linux muss dieser Hostname explizit in den Container-Hosts oder über ein Compose-Extra-Host konfiguriert werden. Mit xdebug.discover_client_host=true versucht Xdebug die Client-IP aus dem HTTP-Header zu lesen – was in manchen Proxy-Setups funktioniert, aber weniger zuverlässig ist als eine explizite IP.
; docker/php/xdebug.ini — Xdebug 3 configuration for Docker development
; Only enable what is needed — each mode adds overhead
; Modes: debug (step debugger), coverage, profile, trace, off
; Use XDEBUG_MODE env var to override without rebuilding the image
xdebug.mode = ${XDEBUG_MODE:-off}
; IDE host: host.docker.internal resolves on macOS and Docker Desktop for Windows
; On Linux: use the Docker bridge IP (usually 172.17.0.1) or add extra_hosts
xdebug.client_host = ${XDEBUG_CLIENT_HOST:-host.docker.internal}
; Default port for Xdebug 3 (changed from 9000 in Xdebug 2)
xdebug.client_port = 9003
; Trigger debug sessions by environment variable or query param
; Allows debugging specific requests without enabling globally
xdebug.start_with_request = trigger
; Limit connection attempts to prevent hanging on missing IDE
xdebug.connect_timeout_ms = 2000
; Show detailed error information in browser (useful for development)
xdebug.show_error_trace = 1
xdebug.var_display_max_depth = 5
xdebug.var_display_max_data = 1024
5. Host-IP-Erkennung auf macOS, Linux und WSL2
Die Host-IP-Erkennung ist der häufigste Grund, warum Xdebug in Docker nicht verbindet. Auf macOS und Docker Desktop für Windows löst der Hostname host.docker.internal automatisch auf die Host-IP auf – das ist der einfachste Weg und sollte immer zuerst ausprobiert werden. Auf Linux existiert dieser Hostname standardmäßig nicht; er muss in der Compose-Datei explizit über extra_hosts oder einen benutzerdefinierten DNS-Eintrag konfiguriert werden.
Eine robuste Alternative für Linux ist das Auslesen der Docker-Gateway-IP zur Laufzeit: docker network inspect bridge --format '{ {(index .IPAM.Config 0).Gateway} }' liefert die IP der Docker-Bridge, über die der Container den Host erreicht. Diese IP kann als Umgebungsvariable XDEBUG_CLIENT_HOST beim Container-Start übergeben werden. Mit der Kombination aus xdebug.mode = ${XDEBUG_MODE:-off} und xdebug.client_host = ${XDEBUG_CLIENT_HOST:-host.docker.internal} in der ini-Datei lässt sich Xdebug in Docker plattformunabhängig konfigurieren, ohne das Image je neu bauen zu müssen.
6. Path-Mapping: der häufigste Fehler erklärt
Path-Mapping ist notwendig, weil die Dateipfade im Container von den Pfaden auf dem Host abweichen. Im Container liegt der Code unter /var/www/html/src/, auf dem Host liegt derselbe Code unter /home/user/projects/myapp/src/. Wenn Xdebug der IDE einen Breakpoint-Hit meldet, gibt es den Container-Pfad an. Die IDE muss diesen Container-Pfad auf den lokalen Pfad übersetzen, um die richtige Datei zu öffnen. Stimmt das Mapping nicht, öffnet die IDE entweder die falsche Datei oder zeigt eine Fehlermeldung.
In PhpStorm wird Path-Mapping unter Run → Edit Configurations → PHP Remote Debug → Server konfiguriert. Der Server muss den gleichen Namen haben, der in der Umgebungsvariable PHP_IDE_CONFIG=serverName=myserver konfiguriert ist – dieser Wert verknüpft die Xdebug-Session mit dem richtigen Server-Profil. In VS Code erfolgt das Path-Mapping in der launch.json unter pathMappings. Ein falsches oder fehlendes Path-Mapping ist typischerweise daran erkennbar, dass Breakpoints gesetzt werden können, aber nie ausgelöst werden oder die IDE nach dem Verbinden die falsche Datei öffnet.
# compose.override.yml — Enable Xdebug without modifying the main compose file
# Use: docker compose -f compose.yml -f compose.override.yml up
services:
php:
environment:
# Enable step debugger mode
XDEBUG_MODE: debug
# On Linux: replace with actual Docker bridge IP
XDEBUG_CLIENT_HOST: host.docker.internal
# Must match PhpStorm server name (Run → Edit Configurations → Server)
PHP_IDE_CONFIG: "serverName=myapp-local"
extra_hosts:
# Linux: add host.docker.internal manually pointing to bridge IP
- "host.docker.internal:host-gateway"
# VS Code launch.json (store in .vscode/launch.json)
# {
# "version": "0.2.0",
# "configurations": [{
# "name": "Listen for Xdebug (Docker)",
# "type": "php",
# "request": "launch",
# "port": 9003,
# "pathMappings": {
# "/var/www/html": "${workspaceFolder}/src"
# }
# }]
# }
7. PhpStorm-Integration Schritt für Schritt
PhpStorm hat die beste native Integration für Xdebug in Docker. Der Einrichtungsprozess umfasst drei Schritte: Server-Konfiguration, Debug-Konfiguration und die Verknüpfung über PHP_IDE_CONFIG. Unter Settings → PHP → Servers wird ein neuer Server mit dem Namen angelegt, der später in PHP_IDE_CONFIG verwendet wird. Dort werden auch die Path-Mappings konfiguriert: lokaler Pfad auf der linken Seite, Container-Pfad auf der rechten Seite. Die Einstellung Use path mappings muss explizit aktiviert sein.
Anschließend wird unter Run → Edit Configurations eine neue PHP Remote Debug-Konfiguration angelegt, die auf den eben erstellten Server zeigt und als IDE-Key den Wert PHPSTORM enthält (oder was auch immer als xdebug.idekey konfiguriert wurde). Mit einem Klick auf den grünen Telefon-Button aktiviert PhpStorm den Listener auf Port 9003 und wartet auf eingehende Xdebug-Verbindungen. Wenn alles korrekt konfiguriert ist und Xdebug im Container im Modus debug läuft, erscheint beim ersten Request ein Popup, das fragt, ob die eingehende Verbindung akzeptiert werden soll.
8. VS Code mit PHP Debug Extension
Für VS Code ist die Erweiterung PHP Debug von Xdebug (Felix Becker / xdebug.org) die Standardlösung für Xdebug in Docker. Die Konfiguration erfolgt ausschließlich in der Datei .vscode/launch.json. Das wichtigste Feld ist pathMappings: Ein JSON-Objekt, bei dem die Container-Pfade die Schlüssel und die lokalen Host-Pfade die Werte sind. Die Variable ${workspaceFolder} zeigt auf den Ordner, in dem VS Code geöffnet ist, und kann für relative Pfade genutzt werden.
Im Gegensatz zu PhpStorm ist bei VS Code kein Server-Name in PHP_IDE_CONFIG nötig, weil die Zuordnung allein über das Path-Mapping geschieht. Der Debug-Listener wird über das Menü Run → Start Debugging oder die Taste F5 gestartet. Ein häufiger Fallstrick bei VS Code ist der hostname-Parameter in der Launch-Konfiguration: Standardmäßig lauscht VS Code nur auf localhost (127.0.0.1). In Docker-Umgebungen muss "hostname": "0.0.0.0" gesetzt werden, damit Verbindungen von Container-IPs akzeptiert werden – sonst verbindet Xdebug erfolgreich zur Host-IP, aber der VS-Code-Listener lehnt die Verbindung ab.
9. Xdebug-Modi im Vergleich
Xdebug 3 unterstützt mehrere Modi, die unterschiedliche Funktionen aktivieren und unterschiedliche Performance-Auswirkungen haben. Die Wahl des richtigen Modus für den jeweiligen Anwendungsfall ist entscheidend für produktive Arbeit mit Xdebug in Docker.
| Modus | Funktion | Performance-Overhead | Anwendungsfall |
|---|---|---|---|
| off | Xdebug deaktiviert | Minimal | Normaler Betrieb, kein Debugging nötig |
| debug | Step-Debugger (Breakpoints) | Mittel (nur bei Trigger) | Interaktives Debugging in der IDE |
| coverage | Code-Coverage-Messung | Hoch (immer aktiv) | PHPUnit mit Coverage-Report |
| profile | Cachegrind-Profiler | Sehr hoch | Performance-Analyse mit KCachegrind |
| develop | Var-Dump-Verbesserungen | Gering | Lesbarere var_dump-Ausgabe im Browser |
Die Kombination XDEBUG_MODE=off im normalen Betrieb und XDEBUG_MODE=debug beim aktiven Debugging über Umgebungsvariablen ist das empfohlene Muster für Xdebug in Docker. Mit xdebug.start_with_request=trigger startet Xdebug Debugging-Sessions nur, wenn ein spezieller Cookie, Query-Parameter oder HTTP-Header gesetzt ist – so kann Xdebug im debug-Modus permanent aktiv bleiben, ohne jeden Request zu instrumentieren. Das macht selektives Debugging möglich, ohne die Umgebungsvariable wechseln und den Container neu starten zu müssen.
Mironsoft
PHP-Entwicklungsumgebungen, Docker-Setup und IDE-Integration
Xdebug in Docker noch nicht zuverlässig am Laufen?
Wir richten Xdebug in euren Docker-Containern ein, konfigurieren Path-Mapping für PhpStorm und VS Code und sorgen dafür, dass Debugging-Sessions reproduzierbar starten – ohne manuelle Eingriffe.
Xdebug-Setup
Dockerfile, ini-Konfiguration, Host-IP-Erkennung und Path-Mapping einrichten
IDE-Integration
PhpStorm und VS Code für Xdebug in Docker konfigurieren und testen
Team-Rollout
compose.override.yml und Dokumentation für das gesamte Entwicklerteam erstellen
10. Zusammenfassung
Xdebug in Docker funktioniert zuverlässig, wenn drei Dinge stimmen: die Host-Adresse ist korrekt konfiguriert und erreichbar, Path-Mapping ist in der IDE eingerichtet, und Xdebug läuft nur im debug-Modus wenn tatsächlich debuggt wird. Der spezielle Hostname host.docker.internal löst das Host-IP-Problem auf macOS und Windows; auf Linux erledigt extra_hosts: host.docker.internal:host-gateway in der Compose-Datei dasselbe. Path-Mapping verbindet Container-Pfade mit Host-Pfaden und ist in PhpStorm unter Server-Konfigurationen, in VS Code in der launch.json zu finden.
Umgebungsvariablen für XDEBUG_MODE machen das Umschalten zwischen Debug- und Normalbetrieb trivial. Eine compose.override.yml hält die Debug-Konfiguration aus der Haupt-Compose-Datei heraus und verhindert, dass versehentlich mit aktivem Xdebug deployed wird. Mit xdebug.start_with_request=trigger und dem Browser-Plugin "Xdebug Helper" kann Debugging selektiv per Request ausgelöst werden, ohne den Container-Modus zu wechseln. Diese Kombination aus Konfiguration, IDE-Setup und bewusster Modus-Steuerung macht Xdebug in Docker zu einem zuverlässigen Werkzeug statt einer Frustrationsquelle.
Docker und Xdebug — Das Wichtigste auf einen Blick
Host-IP
host.docker.internal auf macOS/Windows. Auf Linux: extra_hosts mit host-gateway in der Compose-Datei konfigurieren.
Path-Mapping
Container-Pfad ↔ Host-Pfad in der IDE konfigurieren. PHP_IDE_CONFIG=serverName muss mit dem Server-Namen in PhpStorm übereinstimmen.
Modus-Steuerung
XDEBUG_MODE=off im Normalbetrieb, XDEBUG_MODE=debug beim Debugging. start_with_request=trigger für selektive Sessions.
VS Code
hostname: 0.0.0.0 in launch.json setzen – sonst werden Verbindungen von Container-IPs abgelehnt trotz korrekter Xdebug-Konfiguration.