Solved: Remote XDebug from Dockerized PHP app via SSH Tunnel on Ubuntu

expose:
  - "9000"
  1. Remove the ports: section from docker-compose.yml and define expose
  2. On the remote machine enable GatewayPorts clientspecified in /etc/ssh/sshd_config (then restart the sshd service).
  3. This allows you to create an SSH port forward tunnel which listens on all network interfaces (0.0.0.0 or *) which is required in order for the Docker container's virtual network interface to reach your client machine via the tunnel. By default, the GatewayPorts setting limits remote port forwardings to bind to the localhost interface only which Docker can't "see".

Change SSH connection string to explicitly specify that I wanted to listen on the global interface *:

ssh -R *:9000:localhost:9000 username@example.com

Or with Putty/Kitty

Kitty remote SSH tunnel

After doing all this, I was finally able to connect to Xdebug running inside a Docker container on the remote server from my local machine. 🎉

.vscode/launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch built-in server and debug",
            "type": "php",
            "request": "launch",
            "runtimeArgs": [
                "-S",
                "localhost:8000",
                "-t",
                "."
            ],
            "port": 9000,
            "serverReadyAction": {
                "action": "openExternally"
            }
        },
        {
            "name": "Debug current script in console",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "externalConsole": false,
            "port": 9000
        },
        {
            "name": "Listen for Xdebug 9000",
            "type": "php",
            "request": "launch",
            "port": 9000,
            "pathMappings": {
                "/var/www/html/": "${workspaceRoot}/",
            },
        }
    ]
}

xdebug-php.ini

zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so

xdebug.mode=debug
xdebug.client_port=9000
;xdebug.client_host=dockerhost
xdebug.client_host=xdebug://gateway
xdebug.idekey=PHPSTORM
xdebug.start_with_request = yes

Optionally, if you want to explicitly force Xdebug to connect only to the host machine (meaning you only want to use Xdebug via the SSH tunnel), either:

Add the following section to your service in docker-compose.yml:

     extra_hosts:
       - host.docker.internal:host-gateway

Then set xdebug.client_host=host.docker.internal and xdebug.discover_client_host=Off

or

  • Set xdebug.client_host=xdebug://gateway

P.S. A useful command while debugging this was nc -vz 127.0.0.1 9000 which allowed me to check if my SSH port forward was working on the host machine, at least.

Original credits to WackGet