It is possible to access a service such as PostgreSQL on the localhost of an Ubuntu/Linux host machine from inside of a docker container with a bit of configuration.
First thing here is to understand that there are different docker networking modes, and the method you use to connect to a host service from inside the docker container will differ depending on the networking mode of the container.
Here we will be discussing only the default networking mode
bridge. On linux this mode leverages a network interface on your host called
docker0, and both the docker host and containers get their own IP addresses on that bridge. The host address is static and the container addresses are dynamically assigned when a container is launched.
bridge mode you cannot use
localhost from inside a container to reach the host, instead you can acquire the IP address of the host machine on the
docker0 interface and pass it into the docker container.
Acquire the Host IP
From the host machine, list all network interfaces:
ip address show
Note: While many guides out there would have you use the
ifconfigcommand, that command has actually been deprecated for years, and as of Ubuntu 18.04 it no longer even ships with the default distribution, so here we use the preferred
ip address showcommand instead.
inet address of the
docker0 interface. In the following example it is
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:a6:83:2f:1d 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
To get the host IP address we just clip the
/16 from the end of the CIDR:
Give the Host IP to the Docker Container
To keep the container portable, we should always pass in the address dynamically rather than hard coding it inside our code in the container. With docker there are many ways to do this.
Here I create an environment variable named
DB_HOST in the container that our code will need to read:
docker run -e DB_HOST=172.17.0.1
Here I create a new DNS/hostname called
database inside the container:
docker run --add-host=database:172.17.0.1
Note: You could also use
ip address show docker0and
awkto obtain the host IP address dynamically and build that into the
docker runcommand but I’ll leave that for another time.
Now your local postgres service needs to be configured to allow incoming connections from the docker container. There are two configuration layers that will need to be addressed depending on your setup.
Note: I am running Postgres 10.x on Ubuntu 18.04 with postgres installed via the main repository. The location of your config files may vary depending on your setup. Also note that these files may appear blank unless you use
sudoto open them.
First we need to allow incoming connections from docker via the main postgres configuration file:
listen_addresses parameter controls which network interfaces we listen for connections on, and by default this is commented out and defaults to
localhost only. We need to uncomment it and add the IP address of the
docker0 interface from the previous secion like so:
listen_addresses = 'localhost,172.17.0.1'
Alternatively you could just use
* to accept connections from all local network interfaces. This is a very common practice as authentication is controlled separately as we will see in the next section, and access from outside the Ubuntu host machine is controlled via software and/or hardware firewall. Even so I prefer to list the address explicitly because security is all about redundancy.
Now we need to specify specify authentication rules via the client auth config file:
You need to either modify or add a row in the
# IPv4 local connections section with the address set to the network-wide CIDR of the
docker0 interface on the host. This should be the
inet address we obtained in the last section which still has the
/16 attached to the end. Using this ensures that we will allow incoming connections from all docker containers created on the
docker0 bridge without having to know their specific IP addresses.
In our example we also allow access to all databases from all users and use standard md5 authentication. Modify as required:
# IPv4 local connections: host all all 172.17.0.1/16 md5
Alternatively you could use
0.0.0.0/0 as a wildcard address, and while it is also a common practice here I tend to avoid it because the firewall is possibly the only additional security layer in place at this point and security is all about redundancy.
Now restart the postgres service and you should be good to go:
sudo service postgresql restart