Skip to content

PHP (Xdebug) debugging in a Dev Container fails with a 3-hop remote chain: **Windows VS Code (UI) → Remote-SSH → macOS host → Dev Containers → Docker container.** #11788

Description

@bug-fly00

Bug Report: php-debug adapter spawned but DAP initialize never delivered — Remote-SSH → Dev Containers (Windows client → macOS host → Docker container)

Report to: https://github.com/microsoft/vscode-remote-release/issues (VS Code Remote-SSH + Dev Containers)
Also relevant: https://github.com/xdebug/vscode-php-debug/issues

Summary

PHP (Xdebug) debugging in a Dev Container fails with a 3-hop remote chain:
Windows VS Code (UI) → Remote-SSH → macOS host → Dev Containers → Docker container.

The php-debug adapter process is correctly spawned inside the container with its stdio connected via socketpairs to the container's extension host, but the DAP session never sends initialize to the adapter. The adapter stays idle forever and never binds the Xdebug port (9003), so Xdebug connection attempts fail. The exact same chain works fine for Java debugging (vscode-java-debug), proving the DAP transport channel itself is OK — the failure is specific to this php-debug session.

Driving the php-debug adapter directly (bypassing VS Code) with initialize + launch works perfectly: it binds 127.0.0.1:9003 and logs Listening on { address: '127.0.0.1', family: 'IPv4', port: 9003 }.

Environment

Component Value
VS Code client Windows, server build 585eba7c0c34fd6b30faac7c62a42050bfbc0086 (v1.108.1)
Remote host macOS (Docker Desktop)
Connection chain Windows UI → Remote-SSH → macOS → Dev Containers → container wordpress-wordpress-1
Container OS Linux (WordPress official image, PHP 8.3.33 / Apache mod_php)
Extension xdebug.php-debug 1.40.1 (installed only in container)
Xdebug 3.5.3, mode=debug, start_with_request=yes, client_host=127.0.0.1:9003, xdebug.log=/tmp/xdebug.log
Workspace /var/www/html (container) ↔ ${workspaceFolder}; Dev Container opened over Remote-SSH
Java control case Java Debug (vscode-java-debug) in a different Dev Container over the same 3-hop chain → works

launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for XDebug (WordPress)",
      "type": "php",
      "request": "launch",
      "port": 9003,
      "hostname": "127.0.0.1",
      "log": true,
      "trace": true,
      "pathMappings": {
        "/var/www/html": "/var/www/html"
      }
    }
  ]
}

Steps to reproduce

  1. Windows VS Code, connect Remote-SSH to macOS, then "Reopen in Container" for the WordPress dev container (devcontainer.json using compose.yaml + docker-compose.override.yaml, service wordpress, workspaceFolder /var/www/html, remoteUser root).
  2. Open any PHP file, set a breakpoint, press F5 with the "Listen for XDebug (WordPress)" config.
  3. Status bar shows "Listening for Xdebug" (extension's status bar item), but:
    • no listener on TCP 9003 inside the container (/proc/net/tcp, /proc/net/tcp6: nothing on :232B),
    • adapter process stays in S state (idle epoll) forever,
    • "PHP Debug" output channel stays empty (no Listening on … ever printed),
    • every web request to the container logs Xdebug ERR: Could not connect to debugging client. Tried: 127.0.0.1:9003.

Evidence collected inside the container

1. Adapter is spawned by the container extension host and its stdio is a socketpair to the exthost

$ ps -ef | grep phpDebug
1076 478  ...  node -e delete process.env.ELECTRON_RUN_AS_NODE;require(process.argv[1]) \
      /root/.vscode-server/extensions/xdebug.php-debug-1.40.1/out/phpDebug.js

$ ls -l /proc/1076/fd | grep socket
0 -> socket:[12951007]
1 -> socket:[12951009]
2 -> socket:[12951011]
3 -> socket:[12951013]

$ ls -l /proc/478/fd | grep -E '12951006|12951008|12951010|12951012'   # exthost holds the peers
39 -> socket:[12951006]
43 -> socket:[12951008]
45 -> socket:[12951010]
47 -> socket:[12951012]

So: adapter stdio is wired to the container extension host (PID 478) through socketpairs. If the workbench had sent initialize, it would have been relayed.

2. The adapter never receives any DAP message

  • /proc/1076/stat shows state S (sleeping in epoll), no bind on 9003.
  • Container exthost log shows php-debug activated on onDebugResolve:php and then nothing else:
2026-08-12 02:00:21.095 [info] ExtensionService#_doActivateExtension vscode.debug-server-ready, startup: false, activationEvent: 'onDebugResolve'
2026-08-12 02:00:21.096 [info] ExtensionService#_doActivateExtension xdebug.php-debug, startup: false, activationEvent: 'onDebugResolve:php'

3. Direct DAP drive of the same adapter works perfectly (control experiment)

A minimal Node script spawned phpDebug.js directly inside the container, sent initialize then launch over stdio:

  • initialize → response OK
  • launchListening on { address: '127.0.0.1', family: 'IPv4', port: 9003 }
  • /proc/net/tcp then shows 0100007F:232B ... 0A (listening 127.0.0.1:9003)

So the adapter, Xdebug, container network, launch config and pathMappings are all healthy. The failure is in the VS Code debug-session plumbing between the Windows workbench and the adapter over the nested Remote-SSH → Dev Containers connection.

4. Xdebug side is healthy and dialing the right address

Xdebug 3.5.3 in the container; conf.d/zz-wordpress.ini loads last with client_host=127.0.0.1, client_port=9003, mode=debug, start_with_request=yes. /tmp/xdebug.log (mode 666) records every request:

[18] [Step Debug] INFO: Connecting to configured address/port: 127.0.0.1:9003.
[18] [Step Debug] ERR: Could not connect to debugging client. Tried: 127.0.0.1:9003

What we already ruled out

  • ❌ Stale/dirty container connection → force-recreated the container (--force-recreate), fresh reattach, still reproduces.
  • ❌ Xdebug config, port collisions, firewall inside container, pathMappings, breakpoint registration, extension activation order.
  • hostname: 127.0.0.1 in launch.json (direct drive used the same value and worked).
  • ❌ The adapter itself / a bug in php-debug 1.40.1 logic (direct DAP drive works).
  • ✅ The DAP transport over this 3-hop chain is functional in general (Java debugging works in a parallel Dev Container).

Suspected cause

The php-debug DebugSession is created with a non-file workspace URI (vscode-remote://…) inside a Dev Container opened through Remote-SSH. php-debug's resolveDebugConfiguration contains Dev-Container-specific rewriting for folder.uri.scheme !== 'file' (it rewrites pathMappings ${workspaceFolder}folder.uri.toString()), which is unique among debuggers here and is the only code path that differs from the working Java case. Something in that resolve → session-creation → adapter-spawn handshake over the nested remote connection prevents the workbench from ever sending initialize.

Request

Please investigate why, with Remote-SSH + Dev Containers (3-hop chain), the workbench spawns the php-debug adapter but never sends the DAP initialize request, so the adapter never binds its Xdebug port. Expected: debug session works like plain Remote-SSH (where the same setup is reported working), or at least an actionable error is surfaced.

Attachments / diagnostics that could help

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions