nodeJS
Postgres
Docker
ECONNREFUSED
database-connection-issues

ECONNREFUSED for Postgres on nodeJS with dockers

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

If a Node.js app running with Docker reports ECONNREFUSED when connecting to PostgreSQL, the problem is usually not PostgreSQL syntax or the Node client library. It is usually one of three issues: the app is connecting to the wrong host, the database container is not ready yet, or the connection settings inside the container network are different from the settings on your local machine. Once you check those assumptions in the right order, the fix is usually straightforward.

The Most Common Mistake: Using localhost

Inside Docker Compose, localhost inside the application container means the application container itself, not the database container. If your Node service tries to connect to localhost:5432, it will usually fail unless PostgreSQL is running in the same container.

In Compose, use the service name as the hostname:

yaml
1services:
2  db:
3    image: postgres:16
4    environment:
5      POSTGRES_USER: app
6      POSTGRES_PASSWORD: app
7      POSTGRES_DB: appdb
8
9  api:
10    build: .
11    environment:
12      PGHOST: db
13      PGPORT: 5432
14      PGUSER: app
15      PGPASSWORD: app
16      PGDATABASE: appdb
17    depends_on:
18      - db

Here, db is the correct hostname for the application container.

Minimal Node.js Connection Example

Using the pg package:

javascript
1import pg from "pg";
2
3const client = new pg.Client({
4  host: process.env.PGHOST,
5  port: Number(process.env.PGPORT),
6  user: process.env.PGUSER,
7  password: process.env.PGPASSWORD,
8  database: process.env.PGDATABASE,
9});
10
11await client.connect();
12const result = await client.query("select now()");
13console.log(result.rows[0]);
14await client.end();

If this throws ECONNREFUSED, the next question is whether the database is actually listening yet.

Container Startup Order Is Not Readiness

depends_on controls startup order, but it does not guarantee PostgreSQL is ready to accept connections when the Node app starts.

That means an application can start, try one connection immediately, and fail even though the database container exists.

Use a health check or simple retry logic:

javascript
1import pg from "pg";
2
3async function connectWithRetry(maxAttempts = 10) {
4  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
5    const client = new pg.Client({
6      host: process.env.PGHOST,
7      port: Number(process.env.PGPORT),
8      user: process.env.PGUSER,
9      password: process.env.PGPASSWORD,
10      database: process.env.PGDATABASE,
11    });
12
13    try {
14      await client.connect();
15      return client;
16    } catch (err) {
17      console.log(`DB connect attempt ${attempt} failed`);
18      await new Promise((resolve) => setTimeout(resolve, 2000));
19    }
20  }
21
22  throw new Error("Postgres did not become ready in time");
23}

This is a practical startup hardening step for local development and CI.

Verify From Inside the Containers

When debugging, test the environment from the network where the app runs.

Useful commands:

bash
1docker compose ps
2docker compose logs db
3docker compose exec db pg_isready -U app
4docker compose exec api env | grep '^PG'

If pg_isready says PostgreSQL is healthy and the app still fails, the issue is usually host, port, credentials, or network naming.

Port Mapping Is Often Misunderstood

If both services are in the same Compose network, the app should connect to container port 5432, not the host-mapped port used from your laptop shell. Host port mappings matter for traffic entering Docker from outside, not for one container talking to another on the internal network.

This is another reason localhost plus a mapped host port often fails inside the app container.

Common Pitfalls

  • Using localhost from the Node.js container instead of the database service name.
  • Assuming depends_on means PostgreSQL is ready for connections.
  • Using the host-mapped port instead of the database container port for internal container-to-container traffic.
  • Forgetting to verify the app container's actual environment variables.
  • Debugging only from the host machine instead of from inside the running containers.

Summary

  • In Docker Compose, the Node app should usually connect to PostgreSQL using the database service name, not localhost.
  • 'ECONNREFUSED often means the target host or readiness assumption is wrong.'
  • Add retry logic or health-aware startup handling because depends_on is not enough.
  • Verify connectivity from inside the application container.
  • Keep Docker networking and host-port mapping concepts separate when diagnosing connection failures.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.