Search code examples
dockerdocker-composexdebug

How do I connect to Xdebug running inside a Docker container on a remote server from my local machine?


I have a Docker Compose stack running on a cloud server. One of the services is PHP with Xdebug.

When I run the stack on my local machine, I can use Xdebug just fine. But when I run it on the remote cloud server, no matter how I configure things, I can't seem to get Xdebug to connect back to my local IDE.

I've tried changing various Xdebug settings (discover_client_host etc.) but nothing seems to work.

My latest attempt is as follows:

  1. From docker-compose.yml:
[...]

  environment:
      - XDEBUG_MODE=debug
      - XDEBUG_CONFIG=discover_client_host=On

  ports:
      - 9003:9003

[...]

networks:
  internal:
    driver: bridge
  1. When I run docker compose up on the cloud server I can see that exposes port 9003:
sudo netstat -nlp | grep 9003
tcp        0      0 0.0.0.0:9003            0.0.0.0:*               LISTEN      2119936/docker-prox 
tcp6       0      0 :::9003                 :::*                    LISTEN      2119943/docker-prox 
  1. So then I try to create a port forward via an SSH tunnel from my local machine like this:
ssh -R 9003:localhost:9003 [email protected]

But I get an error: Warning: remote port forwarding failed for listen port 9003 presumably because port 9003 is already in use.

But if that's the case, how can I ever access port 9003 inside the Docker container from my local machine?


Solution

  • This turned out to be quite the puzzle, and several pieces had to fit together rather precisely to solve it:

    1. As per Derick's answer, I removed the ports: section from docker-compose.yml.

    2. On the remote machine I had to enable GatewayPorts clientspecified in /etc/ssh/sshd_config (then restart the sshd service).

      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".

    3. I had to change my SSH connection string to explicitly specify that I wanted to listen on the global interface *:

      ssh -R *:9003:localhost:9003 [email protected]
      

    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. 🎉


    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

    This allows the Docker container to resolve the host machine's internal IP address, as explained in this question and this article.


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