0

I have a django in a docker container that must access a postgres database at my localhost. The Dockerfile works fine when accessing the database residing at an external host, but it can't find the database at my host.

It is a well known problem and there is a lot of documentation, but it doesn't work in my case. This question resembles another question but it did not solve my problem. I actually describe the correct solution of this problem as @Zeitounator pointed out, but it still did not work. It was thanks @Zeitounator that I realised two parts of the problem must be solved: the docker side and the PostgreSQL side. I did not find that solution in any of the answers I read. I did however read about the same frustration: getting a solution that did not work.

It all focuses on which internet address I transmit to the HOST key in the database driver dictionary in the django settings.py:

print('***', os.environ['POSTGRES_DB'], os.environ['POSTGRES_USER'],
        os.environ['POSTGRES_HOST'], os.environ['POSTGRES_PORT'])

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2', # os.environ['ENGINE'],
        'NAME': 'demo',
        'USER': os.environ['POSTGRES_USER'],
        'PASSWORD': os.environ['POSTGRES_PASSWORD'],
        'HOST': os.environ['POSTGRES_HOST'],
    }
}

And running the Dockerfile:

docker build -t dd .
docker run --name demo -p 8000:8000 --rm dd

When POSTGRES_HOST points to my external server 192.168.178.100 it works great. When running python manage.py runserver it finds the host and the database. The server starts and waits for commands. When pointing to 127.0.0.1 it fails (which actually is great too: the container really isolates).

django.db.utils.OperationalError: could not connect to server: Connection refused
    Is the server running on host "127.0.0.1" and accepting
    TCP/IP connections on port 5432?

But as I can connect to my server, I should be able to connect to the localhost IP and that fails just as well (I forgot to tell that the database really runs). When I use host.docker.internal it doesn't work either when running:

docker run --name demo -p 8000:8000 --rm --add-host host.docker.internal:host-gateway dd

It replaces host.docker.internal by 172.17.0.1. The only solution that works so far is:

# set POSTGRES_HOST at 127.0.0.1
docker run --name demo -p 8000:8000 --rm --network host dd

but that seems something I don't want. As far as I understand the documentation it makes the full host stack available to the container which defeats the idea of a container in the first place. But second: it doesn't work in docker-compose, though I specify:

extra_hosts:
  - "host.docker.internal:host-gateway"

network_mode: host leads to a compile error, docker-compose refuses to run.

Is there a way to access a service running on my PC from a docker running on my PC as well from Dockerfile as well as docker-compose and can be deployed as well?

using Ubuntu 21.04 (latest version), Docker version 20.10.7, build 20.10.7-0ubuntu5.1, postgresql v14. My Dockerfile:

FROM python:3

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN mkdir /app

WORKDIR /app

COPY . .

RUN pip install -r requirements.txt

CMD ["python", "manage.py", "runserver", "127.0.0.1:8000"]
Arnold
  • 4,578
  • 6
  • 52
  • 91
  • 1
    `host-gateway` used in your `add_host` stanza will make `host.docker.internal` point to your default docker bridge in both plain command line docker and compose (i.e. `172.17.0.1` in your setup). There is absolutely no reason why you could not contact your postgres server on that IP **as long as your local postgres server is listening on that interface and allowing connections to your server/db from it** – Zeitounator Feb 13 '22 at 09:24
  • 1
    From the point of view of your Django container, 127.0.0.1 is a Django container that isn't running a database; every program on every computer is "running on localhost" but it's a different localhost. You should be able to configure `POSTGRES_HOST=host.docker.internal` (with the `--add-host` option you show on native Linux). Also consider running the local database in a container so you can use native Docker networking and have per-project database servers. – David Maze Feb 13 '22 at 11:41
  • @David, I had already tried what you suggested and it resolved to 172.17.0.1, an address I already found in ifconfig. Sorry I was not clear about that in my question. You are absolutely right about containerizing the database part as well, but if I am not able to solve the problem of accessing my localhost database it's clear that I don't understand the problem very well. That's why I wanted to solve that first. I will now start to do as you suggest :-) – Arnold Feb 13 '22 at 11:47

1 Answers1

2

There are two parts in solving this problem: the docker part and the postgresql part as @Zeitounator pointed out. I was not aware of the postgres part. Thanks to his comment I could resolve this issue. And it works for Dockerfile as well as docker-compose where this Dockerfile is used.

One has to change two postgres configuration files, both are on /etc/postgresql/<version>/main:

postgresql.conf

Change the listen address. Initially it shows:

listen_addresses = 'localhost'      # what IP address(es) to listen on;
                    # comma-separated list of addresses;
                    # defaults to 'localhost'; use '*' for all
                    # (change requires restart)

I changed 'localhost' to '*'. It can be more specific, in this case to the docker address, but as a Proof of Concept it worked.

pg_hba.conf

In my case host.docker.internal resolves to 172.17.0.1. This seems some default docker gateway address as I noticed in most discussion regarding this subject. Add two lines at the very end of the file:

host    all             all              172.17.0.1/0                       md5
host    all             all              ::ffff:ac11:1/0                            md5
Arnold
  • 4,578
  • 6
  • 52
  • 91