Postgres Connection Refused Inside Docker Network

A “connection refused” error between two containers on the same Docker network almost always means the PostgreSQL container isn’t reachable yet, isn’t listening where you expect, or the requesting container is looking in the wrong place entirely. Unlike a “connection refused” from a completely wrong host, this version is deceptively close to working, which makes it more confusing to debug.

The Problem

Your app container tries to connect to PostgreSQL using the service name from docker-compose.yml, and instead of connecting, you get:

psql: error: connection to server at "db" (172.19.0.2), port 5432 failed: Connection refused
Is the server running on that host and accepting TCP/IP connections?

The database container shows as running in docker ps, and the error even shows a resolved IP address, so DNS resolution clearly worked — the connection itself is being actively refused, not just timing out.

Why It Happens

“Connection refused” is a specific signal: it means something is actively rejecting the connection at the TCP level, which is different from a timeout (nothing answering at all) or a DNS failure (name doesn’t resolve). Inside a Docker network, this specific error usually comes down to one of these:

  • PostgreSQL inside the container hasn’t finished starting yet — the process takes a moment to initialize even after the container itself reports as “running”, and any connection attempt during that window gets refused
  • The official PostgreSQL image’s listen_addresses setting is more restrictive than expected in a custom image or override, and it isn’t accepting connections from the container’s network interface
  • The app is connecting to the wrong port because the container’s internal port and the host-mapped port were mixed up (e.g. trying to use a remapped host port like 5433 from inside another container instead of the internal 5432)
  • A healthcheck-less depends_on let the app container start and immediately try to connect before Postgres was ready to accept connections

The Fix

First, confirm Postgres is actually ready to accept connections, not just “running” as a container:

docker compose logs db

Look for the line database system is ready to accept connections — if your app tried to connect before this line appeared, that’s the root cause, not a permanent configuration issue.

Add a healthcheck to the PostgreSQL service and make your app service wait for it properly, rather than just waiting for the container to start:

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
  app:
    depends_on:
      db:
        condition: service_healthy

Next, confirm you’re using the correct port. From another container, always connect using PostgreSQL’s internal port (usually 5432), never the host-mapped port from the ports section of your compose file:

postgresql://user:pass@db:5432/mydb

If the container is genuinely ready and the port is correct but the connection is still refused, check whether a custom postgresql.conf mounted into the container is overriding the default listen_addresses to something narrower than expected, like listen_addresses = 'localhost' instead of '*'.

Still Not Working?

If everything above checks out, try connecting from inside the app container manually with psql to isolate whether the issue is with your application’s connection logic or with the network/database itself:

docker compose exec app psql -h db -U postgres -d mydb

If this manual connection succeeds but your application still fails, the problem is likely in how your app builds its connection string (wrong port, wrong credentials, or reading an outdated environment variable), not in Docker’s networking.

It’s also worth ruling out a stale image as the cause. If you rebuilt your app image after fixing the connection logic but the container is still running an older image under the same tag, Docker Compose may not automatically pick up the change without an explicit rebuild. Force both a rebuild and a fresh container to eliminate this as a variable:

docker compose up -d --build --force-recreate app

Finally, if you’re running this setup in a CI environment or a remote server rather than locally, double-check that the network name Docker Compose generates hasn’t changed unexpectedly — Compose prefixes network names with the project directory name by default, so running the same docker-compose.yml from a differently named folder can result in containers landing on a network with a different name than you expect, even though the compose file itself is identical.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *