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:
docker-compose.yml
:[...]
environment:
- XDEBUG_MODE=debug
- XDEBUG_CONFIG=discover_client_host=On
ports:
- 9003:9003
[...]
networks:
internal:
driver: bridge
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
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?
This turned out to be quite the puzzle, and several pieces had to fit together rather precisely to solve it:
As per Derick's answer, I removed the ports:
section from docker-compose.yml
.
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".
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
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.