Docker Container Can’t Connect to Host PostgreSQL

When a containerized application needs to reach a PostgreSQL server running directly on your host machine (not inside another container), connection failures are almost always caused by networking assumptions that don’t hold true across the container boundary — not by PostgreSQL itself being broken.

The Problem

Your app runs fine locally, connecting to PostgreSQL on localhost:5432. But once you put that same app inside a Docker container, the connection fails with errors like:

could not connect to server: Connection refused
Is the server running on host "localhost" and accepting TCP/IP connections on port 5432?

or, if it gets further before failing:

FATAL: no pg_hba.conf entry for host "172.17.0.2", user "app", database "mydb", no encryption

PostgreSQL is clearly running and accepting connections from your host terminal, but the containerized app can’t reach it the same way.

Why It Happens

The core issue is that localhost inside a container refers to the container itself, not to your host machine. From inside the container, there is no service listening on localhost:5432 — the containerized process is looking for PostgreSQL on its own private network namespace, completely separate from where PostgreSQL is actually running. This trips up a lot of developers moving from local development straight into Docker, because the connection string that worked perfectly a moment ago suddenly points to nothing at all. On top of that, even once you point to the right host address, two more things commonly block the connection:

  • PostgreSQL’s postgresql.conf may only be configured to listen_addresses = 'localhost', which refuses any connection that isn’t literally from the same machine’s loopback interface, regardless of what address or hostname the client used to reach it
  • PostgreSQL’s pg_hba.conf access control file may not have a rule allowing connections from the container’s network range, even if the server itself is listening on the right interface — PostgreSQL checks this file for every incoming connection attempt and rejects anything that doesn’t match an explicit rule

The Fix

First, replace localhost with the correct address for reaching the host from inside a container. On Docker Desktop (Mac and Windows), Docker provides a special DNS name for exactly this purpose:

postgresql://app:password@host.docker.internal:5432/mydb

On Linux, host.docker.internal isn’t available by default in older Docker versions, so you’ll need to either add it manually when running the container:

docker run --add-host=host.docker.internal:host-gateway your-image

or use the Docker bridge gateway IP directly, which you can find with:

ip addr show docker0

Next, make sure PostgreSQL is actually listening on more than just the loopback interface. Edit postgresql.conf (commonly in /etc/postgresql/<version>/main/ on Linux):

listen_addresses = '*'

Then update pg_hba.conf in the same directory to allow connections from the Docker network range, which is typically something like 172.17.0.0/16 by default (confirm with docker network inspect bridge):

host    all    all    172.17.0.0/16    scram-sha-256

Restart PostgreSQL after editing both files for the changes to take effect, since neither file is reloaded automatically just by saving it:

sudo systemctl restart postgresql

Still Not Working?

If you’re on Linux and still can’t connect after these changes, check whether a firewall (like ufw or firewalld) is blocking the port from the Docker bridge network specifically, even though it allows normal host traffic. Docker often creates its own iptables rules that can interact with an existing firewall configuration in ways that are hard to predict just by reading the config files, so testing directly is more reliable than assuming.

It’s also worth double-checking that you’re not accidentally running a second PostgreSQL instance inside another container that’s intercepting the connection on the same port mapping — a surprisingly common source of confusion when a project has both a host-installed database and a leftover docker-compose.yml service also trying to use port 5432. Running docker ps alongside sudo lsof -i :5432 on the host will show you exactly what is actually bound to that port, which clears up this kind of ambiguity quickly.

Leave a Comment