DEV Community

Cover image for Accessing Host Services from Docker Containers
Mohammad Javad Naderi
Mohammad Javad Naderi

Posted on • Updated on

Accessing Host Services from Docker Containers

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
Enter fullscreen mode Exit fullscreen mode

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.

Diagram of Accessing Host Service from Docker Container

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
suriya786 profile image
suriya786

Thank you for the great article.

Collapse
 
mjnaderi profile image
Mohammad Javad Naderi

You're welcome. :)

Collapse
 
jquintanilla4 profile image
jquintanilla4

Mate, cheers for this. I've been looking for a solution like this for days.

Collapse
 
mjnaderi profile image
Mohammad Javad Naderi

Thank you for your feedback!

Collapse
 
itismoej profile image
Mohammad

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 :)

Collapse
 
mjnaderi profile image
Mohammad Javad Naderi

You're welcome! I'm glad that this post was helpful to you.