Docker: How to fix "could not translate host name "postgres" to address" when connecting from one container to another using container name?

Viewed 7324

I start 3 container:

docker run -d --rm --name redis    --publish 6379:6379 redis 
docker run -d --rm --name postgres --publish 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=hi postgres:9.6
docker run -d --rm --name web      --publish 8000:8000 python:3.6.8-stretch sleep 1000

Doing docker ps shows: enter image description here

Then I exec into web:

docker exec -it web bash

Then I install psycopg2:

$ pip install psycopg2

Then I try to connect to the host: postgres, port:5432, database:hi:

I start python:

$ python

Then I run these commands:

import psycopg2
conn = psycopg2.connect(host="postgres", port = 5432, database="hi", user="postgres", password="<passwd>")

And I get the following error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/psycopg2/__init__.py", line 127, in connect
    conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
psycopg2.OperationalError: could not translate host name "postgres" to address: Name or service not known

I thought it might be a docker network issue, so I went to inspect it, and all the hostnames are there:

{
    "Name": "bridge",
    "Id": "8e37606d20ae80189da4d7ecdb7673274fa2e0c9dd2b5c2b4c3db4e545c1ef20",
    "Created": "2020-05-14T19:10:43.532158344Z",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
        "Driver": "default",
        "Options": null,
        "Config": [
            {
                "Subnet": "172.17.0.0/16",
                "Gateway": "172.17.0.1"
            }
        ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
        "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {
        "3bb2a6cf4fbb235324d335f238c20fe026e37bccc717d1cd0bd1df43e70dbec6": {
            "Name": "redis",
            "EndpointID": "f927fc2a199d033bbbc8ecc8ff1bccf508e607384e4820884f3236f11a961c04",
            "MacAddress": "02:42:ac:11:00:02",
            "IPv4Address": "172.17.0.2/16",
            "IPv6Address": ""
        },
        "545065d96fb6f867724ece2cf11881af26be9885f16212bef4260bccba00a2be": {
            "Name": "web",
            "EndpointID": "750133a6bb72e23929c57f32aa7776bc85d3dd255a16473a4afe230d0c6cb9c2",
            "MacAddress": "02:42:ac:11:00:04",
            "IPv4Address": "172.17.0.4/16",
            "IPv6Address": ""
        },
        "cfb2e556ca9f26ff8c7063f2347b81b2a90365cfa476cab8611edf1586404a88": {
            "Name": "postgres",
            "EndpointID": "069411d5c42b3b968ce38b92b7122c608f1dca696517122728f23086fdce53c7",
            "MacAddress": "02:42:ac:11:00:03",
            "IPv4Address": "172.17.0.3/16",
            "IPv6Address": ""
        }
    },
    "Options": {
        "com.docker.network.bridge.default_bridge": "true",
        "com.docker.network.bridge.enable_icc": "true",
        "com.docker.network.bridge.enable_ip_masquerade": "true",
        "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
        "com.docker.network.bridge.name": "docker0",
        "com.docker.network.driver.mtu": "1500"
    },
    "Labels": {}
}

It seems that the hostname postgres should be resolved to the ip of 172.17.0.3.

When I do that in python, it works:

import psycopg2
conn = psycopg2.connect(host="172.17.0.3", port = 5432, database="hi", user="postgres", password="")

How can I connect to the container named postgres using the its name as a host?

2 Answers

You are right in suspecting that this is a docker network issue.

As you discovered, name resolution does not work with the default network being used. To allow for name resolution to work, you'll need to use a separate network like so:

docker network create my-network

Then, run your containers with the --net argument:

docker run -d --rm --name postgres --publish 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=hi --net my-network postgres:9.6
docker run -d --rm --name redis --publish 6379:6379 --net my-network redis
docker run -d --rm --name web --publish 8000:8000 --net my-network python:3.6.8-stretch sleep 1000

Using this user-created network will then allow you to use the container names:

>>> import psycopg2
>>> conn = psycopg2.connect(host="postgres", port = 5432, database="hi", user="postgres", password="")
>>> conn.info.user
'postgres'

Note that for setups using multiple containers I would recommend to use docker-compose instead of just docker commands, where this network is set up in the background for you.

Seems like the issue is that all the containers were connecting to the default bridge network, which only allows resolution by IP address. Documentation:

Containers on the default bridge network can only access each other by IP addresses, unless you use the --link option, which is considered legacy.

For resolution by service name, I need to create my own network first, and then connect all the containers to the network.

On a user-defined bridge network, containers can resolve each other by name or alias

The commands should have been

docker create network my-network

docker run -d --rm --network my-network --name redis    --publish 6379:6379 redis 
docker run -d --rm --network my-network --name postgres --publish 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=hi postgres:9.6
docker run -d --rm --network my-network --name web      --publish 8000:8000 python:3.6.8-stretch sleep 1000
Related