Blog / Article #116
Xdebug dans Docker : le frein à main oublié qui transforme votre PHP en escargot

star

04 Septembre 2026
Xdebug dans Docker php

Le problème ? Xdebug est activé par défaut dans bon nombre d’images Docker PHP officielles ou populaires. Pas parce que c’est une bonne idée, mais parce que c’est pratique. Enfin, soyons précis : les images PHP officielles ne l’installent pas directement, mais beaucoup d’images de développement, de templates Docker et de configurations maison le font. Une fois ajouté, il reste souvent activé en permanence, comme cette extension Chrome installée en 2017 dont plus personne ne connaît l’utilité.

Même sans breakpoint, il peut travailler

On pourrait croire que Xdebug ne fait rien tant qu’on ne pose pas un breakpoint dans PHPStorm. Ce serait logique. Mais selon sa configuration, il peut analyser les appels de fonctions, collecter des informations, générer des traces ou tenter de contacter votre IDE à chaque requête.

Avec Symfony, l’effet peut devenir particulièrement visible. Entre le conteneur de services, les listeners, les attributs, les proxies Doctrine et les trente-sept couches traversées pour afficher « Bonjour », Xdebug a largement de quoi remplir ses journées. Une page qui répondait en 150 millisecondes peut soudain prendre une seconde ou davantage, et tout le monde commence à regarder Redis d’un air soupçonneux.

Le piège du start_with_request=yes

La configuration la plus brutale consiste à demander à Xdebug de démarrer une session sur chaque requête :

xdebug.mode=debug
xdebug.start_with_request=yes

C’est confortable pendant cinq minutes. Ensuite, chaque appel AJAX, chaque requête vers la toolbar Symfony et chaque commande lancée dans le conteneur peut tenter de joindre l’IDE. Si celui-ci n’écoute pas ou si xdebug.client_host pointe vers une adresse inaccessible, Xdebug attend avant d’abandonner. Quelques centaines de millisecondes ici, quelques centaines là, et votre application donne l’impression de tourner sur un Raspberry Pi oublié derrière une box Internet.

Une configuration plus raisonnable consiste à ne démarrer le debug qu’en présence d’un déclencheur :

xdebug.mode=debug
xdebug.start_with_request=trigger

La session peut alors être activée depuis l’extension du navigateur, un cookie, un paramètre ou une variable d’environnement. Xdebug reste disponible, mais il arrête de suivre toutes vos requêtes comme un stagiaire beaucoup trop motivé.

Le bon réflexe : désactivé par défaut

La solution n’est pas de supprimer Xdebug et de revenir au glorieux var_dump() suivi d’un die(). Il suffit de le laisser installé, mais inactif tant qu’on n’en a pas besoin :

xdebug.mode=off

Pour lancer ponctuellement une commande avec le débogueur :

docker compose exec -e XDEBUG_MODE=debug php \
    php bin/console app:ma-commande

Et pour PHPUnit avec la couverture de code :

docker compose exec -e XDEBUG_MODE=coverage php \
    php bin/phpunit --coverage-text

Le mode coverage est particulièrement coûteux, puisqu’il doit observer les lignes exécutées pendant les tests. Il est donc parfaitement normal que la suite soit plus lente lorsqu’on produit un rapport. Ce qui l’est moins, c’est de conserver ce mode pour afficher la page d’accueil toute la journée.

Une image de développement, une image de production

Xdebug n’a rien à faire dans l’image finale de production. Le plus propre est d’utiliser une cible Docker dédiée au développement, dans laquelle l’extension est installée, et une cible de production qui ne la contient simplement pas.

On évite ainsi les performances dégradées, les fichiers de configuration oubliés et le fameux « pourtant, sur ma machine, ça allait vite ». On réduit aussi la taille de l’image et le nombre de composants inutiles embarqués en production. Ce n’est pas spectaculaire, mais la production apprécie rarement les surprises spectaculaires.

Avant d’acheter un nouveau serveur

Si votre application Docker semble inexplicablement lente, commencez donc par vérifier Xdebug :

docker compose exec php php --ri xdebug

Regardez notamment les valeurs de xdebug.mode et xdebug.start_with_request. Cela prend trente secondes et peut éviter une enquête complète impliquant Symfony, Doctrine, Docker, Linux, le SSD, le réseau et Mercure, qui n’avait pourtant rien demandé.

Xdebug reste un excellent outil. Mais comme une perceuse, on l’allume quand on doit percer un trou. On ne la laisse pas tourner toute la journée sur le bureau en se demandant pourquoi il y a du bruit, pourquoi tout tremble et pourquoi la batterie est vide.

Publié le 04/09/2026 · 4 min de lecture Partager X LinkedIn

← Précédent Sonarqube et le coverage en ci : le combat perdu d’avance ? Suivant → Badges dans le readme : le bling-bling utile (ou pas) pour ton projet