skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

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

    1. I had to add the following section to my service in docker-compose.yml:
        extra_hosts:
          - host.docker.internal:host-gateway
    

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

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

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

    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.


  2. If you set your docker container to expose port 9003, it will open that port. Which means that your ssh -R command can’t open that same port.

    You need to remove:

    ports:
          - 9003:9003
    

    And then your ssh -R 9003... command will work.

    As Xdebug needs to connect to your machine, it also means that it needs to connect to this port 9003 that your ssh -R sets up. If you set xdebug.discover_client_host=1, then it will try to use your local machine’s global IP address, which will likely not work. Instead, you need to set xdebug.discover_client_host=0.

    The ssh tunnel that you setup basically creates (from the POV of your docker container) a port 9003 on the host machine. If that is already in the docker container too, then you can set xdebug.client_host=localhost.

    I believe however, that your SSH opens a shell on the host where docker runs in. In which case you need to set xdebug.client_host to the IP address of it’s local gateway, or, if you’re using xdebug 3.2, you can set xdebug.client_host=xdebug://gateway.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search