Solución de problemas con xdebug “doble” en Docker PHP

En este tema, para los usuarios con problemas de Xdebug que aparece “doble” en phpinfo() o xdebug_info(), o que no conecta bien con el IDE. Vamos a modificar algunos pasos de la instalación para ver si solucionamos el problema.

1) Dockerfile

Deja que el Dockerfile habilite Xdebug con el script oficial y no vuelvas a declarar zend_extension a mano.

# [...]
# Instalar Xdebug con PECL y habilitarlo (esto crea docker-php-ext-xdebug.ini)
RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends $PHPIZE_DEPS; \
    pecl install xdebug; \
    docker-php-ext-enable xdebug; \
    apt-get purge -y --auto-remove $PHPIZE_DEPS; \
    rm -rf /var/lib/apt/lists/*
# [...]

Nada de zend_extension en el Dockerfile. Ese marrón ya lo asume docker-php-ext-enable.

2) conf.d/99-xdebug.ini

Este archivo debe tener solo directivas, sin zend_extension. Algo así:

; NO pongas zend_extension aquí
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_port=9003
; Para equipos heterogéneos suele ser mejor:
xdebug.discover_client_host=true
; Si prefieres host fijo, comenta la anterior y usa:
; xdebug.client_host=host.docker.internal

; Durante diagnóstico, súbelo:
; xdebug.log_level=7
; xdebug.log=/tmp/xdebug.log

3) Limpieza de duplicados antiguos (una vez)

Si ya venías arrastrando cosas, entra al contenedor PHP y quita cualquier ini extra que declare la extensión además del que crea ext-enable:

# Ver qué hay
docker compose exec php sh -lc 'ls -1 /usr/local/etc/php/conf.d | cat'

# Borrar el tuyo antiguo si aún tiene zend_extension
docker compose exec php sh -lc 'sed -n "1,3p" /usr/local/etc/php/conf.d/99-xdebug.ini'

# Si ves "zend_extension" ahí, elimínalo del archivo (o sustitúyelo por el contenido de arriba).
# Si por lo que sea hay otro ini casero con zend_extension, bórralo:
# docker compose exec php rm -f /usr/local/etc/php/conf.d/20-xdebug.ini

Lo normal es quedarte con:

  • docker-php-ext-xdebug.ini → contiene el zend_extension=...
  • 99-xdebug.ini → solo xdebug.*

4) (Recomendado) Para Linux

En docker-compose.yml, servicio php, mete el gateway para que “host.docker.internal” exista. Si usas discover_client_host=true puedes vivir sin esto, pero no estorba:

services:
  php:
    # ...
    extra_hosts:
      - "host.docker.internal:host-gateway"

¿Rearrancar o rebuild?

Depende de qué hayas tocado. Regla simple:

  • Si cambiaste el Dockerfile (instalación/habilitación de Xdebug): necesitas rebuild de la imagen.
docker compose down
docker compose build --no-cache php
docker compose up -d
  • Si solo editaste archivos .ini montados por volumen (o copiados fuera del build): basta reiniciar el contenedor PHP para que FPM recargue INIs.
docker compose restart php
# si eres de puño de hierro:
# docker compose exec php kill -USR2 1   # reload maestro de php-fpm

No intentes rezarle a php --ini sin reiniciar FPM si cambiaste INIs dentro del contenedor; a FPM le da igual hasta que lo reinicias.


Verificación en 30 segundos

Dentro del contenedor PHP:

# 1) Ver que Xdebug está cargado en CLI
docker compose exec php sh -lc 'php -m | grep -i xdebug'

# 2) Ver que FPM carga lo mismo (no te fíes solo de CLI)
docker compose exec php sh -lc 'php-fpm -i | grep -i "xdebug.mode\|client_host\|client_port\|start_with_request\|discover_client_host"'

# 3) Si todo ok, en el navegador abre /xdebug.php con:
#    <?php xdebug_info();
#    y comprueba client/port y si "intentó conectar".