Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Connect to a Service Running on Your Docker Host from a Container

blog/cards/connect-to-a-service-running-on-your-docker-host-from-a-container.jpg

This could be useful if you want to connect to a database or something else not running in Docker.

Quick Jump: Modern Versions of Docker | Trying It Out with Ping | Ancient Versions of Docker | Demo Video

I’ve written about this topic back in 2017 and 2018 but a few things have changed since then to make this process easier no matter how you run Docker.

Here’s 2 use cases of when you might want to do this:

Maybe you’re transitioning into using Docker and you’re not ready to move your database into Docker yet so you want to connect to a locally running version of PostgreSQL, MySQL or whatever DB you have.

Or, perhaps you have a few services that you’re in the process of Dockerizing. 2 of them are in Docker but the 3rd service isn’t running in Docker yet but your other 2 services need to connect to this 3rd service.

Modern Versions of Docker

All of the options below let you use Docker’s bridge network without having to resort to using --net=host.

We’re going to take advantage of using host.docker.internal which is a special DNS name available in your container that resolves to your Docker host’s local bridge network gateway IP address. We’ll cover that in more detail once we see it in action by running a few commands.

For example, if you wanted to connect to Postgres running on port 5432 you can configure your Dockerized app to connect to your local Postgres service on host.docker.internal:5432.

Docker Desktop

If you’re using Docker Desktop that hostname works out of the box.

The above feature isn’t new. A few sources indicate that’s been available since Docker Engine 18.03 which was released in March 2018. Chances are you have that!

Native Linux in Production

If you’re not using Docker Desktop, such as native Linux in production you have a feature that’s available in Docker Engine 20.10+ (released in December 2020).

It depends on using host-gateway which is a special value that you can use with --add-host to get your Docker bridge network’s gateway IP address. The basic idea here is you can use that combined with adding the host.docker.internal hostname to make things work the same as Docker Desktop.

Not using Docker Compose?

If you’re running your container directly with the docker CLI you can add this flag --add-host="host.docker.internal:host-gateway" to your app’s container command.

Using Docker Compose?

In your app’s service you can set this property extra_hosts: ["host.docker.internal:host-gateway"] which does the same thing as above.

Trying It Out with Ping

The above goes over how it all works but some of the details are a little abstract, especially with terms like bridge networks and gateway IP addresses.

Try running this container:

docker container run --rm \
  --add-host="host.docker.internal:host-gateway" \
  debian:stable-slim \
  bash -c "apt-get update && apt-get install -y iputils-ping && ping -c 3 host.docker.internal"

No matter how you’re running Docker (Docker Desktop or native) you should see this:

PING host.docker.internal (172.17.0.1) 56(84) bytes of data.
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=1 ttl=64 time=0.039 ms
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=2 ttl=64 time=0.054 ms
64 bytes from host.docker.internal (172.17.0.1): icmp_seq=3 ttl=64 time=0.038 ms

--- host.docker.internal ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2105ms
rtt min/avg/max/mdev = 0.038/0.043/0.054/0.007 ms

But, if you’re not using Docker Desktop and you tried to run the above without the --add-host flag then you’d get this error ping: host.docker.internal: Name or service not known.

That’s because --add-host will add this entry to your container’s /etc/hosts file, such as 172.17.0.1 host.docker.internal. Without that flag then host.docker.internal can’t be looked up and it fails.

By the way, that 172.17.0.1 IP address is what’s associated with the output of running ip a or ifconfig for your docker0 network interface, your IP address may be different. That’s the bridge network’s gateway IP address and is what host.docker.internal maps back to.

You can directly ping or access that IP address instead, even without using --add-host but using DNS is nice here to avoid needing to remember that IP address and on some systems it might not be that IP address depending on how your network is set up.

You can dive 1 level deeper and run docker network inspect bridge to see the output. About a third of the way down you’ll see "Gateway": "172.17.0.1".

With that said, that leads us into how you used to be able to connect to your Docker host before these modern features were available from Docker.

Ancient Versions of Docker

If you’re locked into a really old version of Docker you have a couple of options.

You can run docker network inspect bridge -f "{{range .IPAM.Config}}{{.Gateway}}{{end}}" to get the gateway IP address by itself. We’re using Go’s templating system to parse the IP address.

Instead of using host.docker.internal you can access that IP directly. On my machine it returns 172.17.0.1 but it’s not guaranteed to be that on your system, it depends on how your network set up.

I would use the above solution since it doesn’t require external dependencies or custom parsing any output to get the IP address but alternatively you can run ip a or your operating system’s comparable command (ifconfig, etc.) to find the docker0 network interface.

On my machine ip a includes this output:

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:f0:24:c9:09 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:f0ff:fe24:c909/64 scope link
       valid_lft forever preferred_lft forever

On line 3 you can see the same 172.17.0.1 IP address. This post from 2018 goes into parsing that output in case you need to script out a solution to grab the IP address.

The demo video below covers everything in this post and running some of the commands.

Demo Video

Timestamps

  • 1:04 – Going over a few common use cases
  • 1:56 – What is a modern version of Docker?
  • 2:38 – What is host.docker.internal?
  • 3:18 – Getting this to work with and without Docker Desktop
  • 3:53 – What is host-gateway?
  • 4:17 – Using host-gateway with the Docker CLI and Docker Compose
  • 6:24 – Demoing things out with ping
  • 7:51 – Showing what happens without adding host.docker.internal
  • 8:39 – Network interfaces, gateway IP addresses and DNS
  • 10:04 – Inspecting Docker’s bridge network
  • 10:45 – Stuck on an ancient version of Docker?

When was the last time you’ve had to do this? Let us know below.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per month (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments