Docker und Xdebug: sauberes Setup ohne jeden Schmerz
AI generated
Docker · Xdebug · PHP · PhpStorm · VS Code
Docker und Xdebug: sauberes Setup ohne jeden Schmerz
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.

11 Min. Lesezeit Xdebug 3 · Path-Mapping · PhpStorm · VS Code · Docker PHP 8.x · Docker Engine 26+ · Compose v2

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.

11. FAQ: Docker und Xdebug

1Warum verbindet Xdebug nicht zur IDE?
Häufigste Ursachen: falsche Client-Host-Adresse, Firewall blockiert Port 9003, oder IDE-Listener ist nicht aktiv. host.docker.internal verwenden und Listener-Status prüfen.
2Xdebug 2 vs. Xdebug 3?
Xdebug 3 hat remote_enable und remote_host durch xdebug.mode und xdebug.client_host ersetzt. Port hat sich von 9000 auf 9003 geändert. Alte Konfigurationen funktionieren nicht ohne Anpassung.
3Xdebug nur für bestimmte Requests?
start_with_request=trigger setzen. Dann startet Xdebug nur bei Cookie XDEBUG_SESSION oder Header X-Xdebug-Trigger. Browser-Plugins wie "Xdebug Helper" setzen den Cookie automatisch.
4Was ist host.docker.internal?
Spezieller Hostname von Docker Desktop (macOS/Windows), der auf die Host-IP zeigt. Auf Linux via extra_hosts: host.docker.internal:host-gateway in Compose hinzufügen.
5Breakpoints werden nicht getroffen?
Verbindung prüfen (php -i | grep xdebug), dann Path-Mapping kontrollieren. In PhpStorm muss "Use path mappings" explizit aktiviert sein.
6Verlangsamt Xdebug den Dev-Server?
Im Modus off minimal. Im Modus debug mit trigger nur bei explizit getriggerten Requests. Coverage und profile erzeugen konstanten Overhead.
7CLI-Skripte debuggen?
XDEBUG_MODE=debug XDEBUG_SESSION=1 php script.php. Bei CLI funktioniert der Trigger über Umgebungsvariable statt Cookie.
8PhpStorm öffnet die falsche Datei?
Path-Mapping falsch oder fehlend. Run → Edit Configurations → Server: "Use path mappings" aktivieren und Container-Pfad korrekt auf lokalen Pfad mappen.
9Was ist PHP_IDE_CONFIG?
PHP_IDE_CONFIG=serverName=myserver teilt Xdebug mit, welches Server-Profil in PhpStorm genutzt werden soll. Muss mit dem Server-Namen unter Settings → PHP → Servers übereinstimmen.
10Xdebug mit VS Code in Docker?
PHP Debug Extension, launch.json mit port: 9003, pathMappings und hostname: 0.0.0.0. Ohne hostname: 0.0.0.0 werden Container-Verbindungen trotz korrektem Xdebug abgelehnt.