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.

Networking 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.

In 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.

Note: As of docker 18.03+, those running docker for windows and docker for mac can use host.docker.internal from inside the container to access the host IP, in development environments.

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 ifconfig command, 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 show command instead.

Note the inet address of the docker0 interface. In the following example it is 172.17.0.1/16:

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

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 docker0 and awk to obtain the host IP address dynamically and build that into the docker run command but I’ll leave that for another time.

PostgreSQL Configuration

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 sudo to open them.

postgresql.conf

First we need to allow incoming connections from docker via the main postgres configuration file: /etc/postgresql/10/main/postgresql.conf

The 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.

pg_hba.conf

Now we need to specify specify authentication rules via the client auth config file: /etc/postgresql/10/main/pg_hba.conf

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