Accessing a host service from within a Docker container is a common requirement. For instance, you might have a database running on the host system at 127.0.0.1:5432
(or a HTTP proxy at 127.0.0.1:8118
) that you want to access from a Docker container.
One straightforward solution is to use --network host
for the Docker container or network_mode: host
in Docker Compose. However, this approach can lead to complications. If you use network_mode: host
for one service, you may find yourself needing to use it for other services or publish their ports just to enable them to communicate with each other. This can quickly result in losing the benefits of Docker Compose's default network.
A more elegant solution is to connect to the special DNS name host.docker.internal
, which resolves to the internal IP address used by the host (for example, using host.docker.internal:5432
to connect to the database). On Linux, you need to pass --add-host=host.docker.internal:host-gateway
to the docker
command. The Docker Compose equivalent is:
extra_hosts:
- host.docker.internal:host-gateway
However, this approach requires binding the database server to 0.0.0.0
or the Docker host IP (host.docker.internal
, typically 172.17.0.1
). If your service is listening on 127.0.0.1
, this method won't work.
So it won't work for our database example, which is listening on 127.0.0.1:5432
. One workaround is to bind the database server to 0.0.0.0
, but this exposes it to the outside world, which is often undesirable. The other workaround is to bind the database server to 172.17.0.1
, but other apps that rely on 127.0.0.1:5432
will break.
Solution using Socat
I found a simple and effective solution to this problem using the socat
command. Socat is a command-line utility that establishes two bidirectional byte streams and transfers data between them. In this context, it can be used to listen on a TCP port on host.docker.internal
and forward to the database server. This allows you to connect to the database using host.docker.internal
.
Here's an example of how to set this up in Docker Compose:
services:
database-relay:
image: alpine/socat:latest
network_mode: host
command: TCP-LISTEN:5432,fork,bind=host.docker.internal TCP-CONNECT:127.0.0.1:5432
extra_hosts:
- host.docker.internal:host-gateway
my-service:
image: some-image
command: my-command --connect-to host.docker.internal:5432
extra_hosts:
- host.docker.internal:host-gateway
In this configuration, the database-relay
service listens on host.docker.internal:5432
and forwards to 127.0.0.1:5432
. This allows my-service
to access the database without using network_mode: host
and without changing the bind address of the database.
Did you find this post useful? Please leave your feedback in the comments. 😊️
Top comments (6)
Thank you for the great article.
You're welcome. :)
Mate, cheers for this. I've been looking for a solution like this for days.
Thank you for your feedback!
Awesome! I used it to port my proxy, which was alive in my host machine, to my containers without giving them access to the host network. Thanks Mohammad Javad :)
You're welcome! I'm glad that this post was helpful to you.