BugMojoBugMojoBugMojo
FeaturesPricingBlogHelpAbout
Add to ChromeLog inGet started
BugMojoBugMojo

Bug reports that actually help fix bugs — capture, replay, share.

A product of Softech Infra.

Product

  • Features
  • Pricing
  • Browser extension
  • Get started
  • Log in

Resources

  • Help & guides
  • Blog
  • Compare
  • Glossary

Company

  • About
  • Contact
  • Security
  • Privacy
  • Terms
  • Sitemap
© 2026 BugMojo. All rights reserved.
AllGuidesEngineeringPlaybooksCompareGlossaryAlternativesBy roleBug tracking by framework
  1. Home
  2. Blog
  3. Guides
  4. How to Fix "ECONNREFUSED" in Node.js
Guide

How to Fix "ECONNREFUSED" in Node.js

Error: connect ECONNREFUSED means something reached the host and port but nothing was listening — or the connection was actively rejected. Here is what causes it and how to fix every case.

Hrishikesh BaidyaHrishikesh Baidya·Jul 16, 2026·9 min read
Guides
Isometric line-art of a client connection refused, forking into its causes — server not running, wrong host or port, and firewall — one branch lime, on a dark canvas
TL;DR
  • What it means: connect ECONNREFUSED 127.0.0.1:5432 — your process reached the host and port, but nothing there accepted the connection. The address is right and reachable; no service is listening (or it rejected you).
  • Not the same as a timeout: ETIMEDOUT means no response at all (firewall dropping packets, host down); ENOTFOUND means DNS couldn't even resolve the hostname. ECONNREFUSED is a fast, definitive "nobody's home on that port".
  • Most common cause: the target service (database, Redis, another API, your own backend) simply isn't running yet.
  • Fix it: confirm the service is up and listening, check the host/port/env, verify the bind address and Docker networking, and add retry-on-boot for startup races.

Error: connect ECONNREFUSED 127.0.0.1:5432 is one of the clearest error messages Node.js will ever give you — it even prints the exact host and port that failed — and yet it still stops people cold, because the message tells you what happened without telling you why. The good news is that a refused connection narrows the problem down a lot: the network path worked, the address was valid, and something on the other end said no. This guide walks through what the error actually means, how it differs from its two lookalikes, the six things that cause it, and exactly how to fix each one.

What ECONNREFUSED actually means

ECONNREFUSED means your client reached the target host and port, but the connection was actively refused — nothing is listening there, or the service rejected the TCP handshake. Unlike a timeout, the refusal comes back immediately, so you know the address is correct and reachable and the problem is the listener, not the network path.

At the operating-system level, when Node.js opens a TCP socket it sends a connection request to host:port. If a service is listening there, it accepts. If nothing is listening — or a firewall on the host itself sends back a reset — the kernel returns the ECONNREFUSED error, which Node surfaces on the socket's 'error' event. The presence of a host and a port in the message is the key clue: a port is just the number that identifies which listening service on a machine should receive the traffic, so a refusal on 127.0.0.1:5432 means "I found machine 127.0.0.1, asked for whoever owns port 5432, and got nobody".

ECONNREFUSED vs ETIMEDOUT vs ENOTFOUND

These three errors fail at three different stages of making a connection, and telling them apart instantly narrows down the cause:

  • ENOTFOUND — DNS resolution failed. The hostname never turned into an IP address, so no connection was even attempted. Usually a typo in the host, a missing /etc/hosts entry, or an unresolvable Docker service name.
  • ETIMEDOUT — the host was found and a connection was attempted, but no answer ever came back. Packets are being silently dropped, typically by a firewall or security group, or the host is down entirely.
  • ECONNREFUSED — the host answered immediately and said no. The IP is right, the machine is up and reachable, but no service is listening on that port. The most specific of the three, and usually the quickest to fix.

If you see ECONNREFUSED, you can stop suspecting DNS and firewalls-that-drop and focus on "is the thing I'm connecting to actually running, and am I pointed at the right port?"

The six causes (and their fixes)

In rough order of how often they show up:

1. The target service isn't running. This is by far the most common cause. The database, Redis, another internal API, or your own backend simply isn't up. Nothing is listening on the port, so every connection is refused. The fix is to start the service — but first, confirm that's really the problem.

check-the-port
# Is anything listening on the port at all? (macOS / Linux)
lsof -i :5432
# → empty output means nothing is listening — that's your refusal

# For containers, is the service actually up and its port mapped?
docker ps
# Look for a row with 0.0.0.0:5432->5432/tcp; if it's missing, the DB isn't running

# Prove connectivity without your app in the way:
nc -zv 127.0.0.1 5432
# → "succeeded" = something is listening; "Connection refused" = nothing is

2. Wrong host or port. The service is running, but you're knocking on the wrong door. The classic version is connecting to localhost when the service lives in a Docker container that must be reached by its Compose service name, or using 5432 when your database is actually mapped to 5433 on the host. Fix it by comparing your connection string against where the service really listens (the docker ps output above shows the real mapping).

3. The service is bound to the wrong interface. A process that binds to 127.0.0.1 only accepts connections from the same machine (or the same container). If your app is in a different container or on a different host, those connections are refused even though the service is "up". The fix is to bind the service to 0.0.0.0 so it accepts connections on all interfaces.

4. A firewall or security group is blocking the port. On cloud hosts especially, the service can be running and correctly bound, but a security group or local firewall closes the port to your source. This more often produces ETIMEDOUT, but a host-local firewall that actively rejects will produce ECONNREFUSED. Open the port for the right source range.

5. The service is still starting up. In docker-compose, your app container frequently boots faster than the database it depends on, so its first connection attempts hit a port that isn't listening yet. This is a race, not a misconfiguration — and the fix is to retry.

6. Wrong environment variable or connection string. A DATABASE_URL that still points at localhost in an environment where the database is remote, an unset variable that falls back to a default port, or a stale .env file all resolve to "connecting to a place with no listener". Log the exact host and port you're dialing before you connect, and the mismatch usually jumps out.

How to diagnose it step by step

Work outward from the listener to the network to your config:

  1. Confirm the service is up and listening with lsof -i :PORT, docker ps, or a health check.
  2. Confirm the host, port, and env your app is actually using — log the resolved connection string, don't trust the code you think is running.
  3. Test raw connectivity with nc -zv host port or curl so you know whether the problem is the network or your application layer.
  4. Check the bind address and Docker networking — 0.0.0.0 vs 127.0.0.1, and service names vs localhost.
  5. Add retry-on-boot if the target is a dependency that starts on its own schedule.

That last step deserves real code, because a startup race is the one cause you fix in your app rather than your infrastructure. A short retry-with-backoff wrapper turns a fatal boot-order crash into a few seconds of waiting:

connect-with-retry.jsjavascript
// Retry a connection with exponential backoff so a slow-starting
// dependency (DB, Redis) doesn't crash the process on boot.
async function connectWithRetry(connect, { retries = 8, baseMs = 250 } = {}) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      return await connect();
    } catch (err) {
      // Only retry the transient "nobody's listening yet" case
      if (err.code !== 'ECONNREFUSED' || attempt === retries) throw err;
      const wait = baseMs * 2 ** (attempt - 1); // 250, 500, 1000, ...
      console.warn(`ECONNREFUSED — retry ${attempt}/${retries} in ${wait}ms`);
      await new Promise((r) => setTimeout(r, wait));
    }
  }
}

// Usage: pass a function that opens the connection and resolves when ready.
const db = await connectWithRetry(() => pool.connect());

If the refusal is a Docker Compose boot-order race, the more declarative fix is to make your app wait for the dependency to report healthy before it starts. depends_on alone only waits for the container to start, not for the service inside to be ready — so pair it with a healthcheck and the service_healthy condition:

docker-compose.ymlyaml
services:
  db:
    image: postgres:16
    healthcheck:
      # Postgres is "ready" only when this exits 0
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 2s
      timeout: 3s
      retries: 10
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy   # wait for the healthcheck, not just start
    environment:
      # Use the SERVICE NAME as the host, never localhost, inside Compose
      DATABASE_URL: postgres://postgres@db:5432/app
Tip

Notice the two Docker fixes in that file working together: condition: service_healthy removes the startup race, and db:5432 (the service name, not localhost:5432) removes the wrong-host cause. Getting both right eliminates the large majority of Docker ECONNREFUSED reports.

Related failure modes

ECONNREFUSED often travels with its inverse and its browser-side cousin. If you're getting a refusal because you tried to start your own server but the port was already taken by a stale process, that first process failed with EADDRINUSE (port already in use) — worth checking when a service "won't start" and then connections to it are refused. And when the refusal happens between a browser and your API rather than between two backend services, it surfaces in the browser as a network error, which is covered in how to fix "Failed to fetch". Same root idea — nothing accepted the connection — different layer.

Key takeaway

The honest takeaway: ECONNREFUSED is the network telling you the address was right and the machine was reachable, but no service accepted the connection on that port. Don't chase DNS or firewalls first — confirm the target is running and listening, then verify you're pointed at the correct host and port, then handle boot-order races with a retry or a healthcheck. Nine times out of ten it's cause #1 or #2.

The single hardest part of an ECONNREFUSED report is usually reconstructing the exact host:port and environment where the refusal happened — because "it works locally" almost always means your local process is dialing a different address than the one that failed in staging or in a container. Capturing that context at the moment of failure is what separates a five-minute fix from an afternoon of guessing.

Tip

When a refusal only shows up for a teammate or in one environment, the fastest path is to capture the exact request context rather than trying to reproduce it. A tool that records the console error, the network attempt, and the environment where it happened hands you the precise host:port that was refused — which instantly tells you whether you're looking at a config problem (wrong host) or a service-down problem (right host, nothing listening).

⁓ ⁓ ⁓
See the exact host and port that got refused

Install the free BugMojo extension and capture the session — console, network, and environment — when a connection is refused, so you know instantly whether it's a wrong-config bug or a service that's simply down.

Install the free extension

Frequently asked questions

Frequently asked questions

Sources

  1. Errors — Node.js documentation: ECONNREFUSED and the common system errors reported by the net and dns modules — Node.js (2026)
  2. Net — Node.js documentation: TCP socket connection lifecycle, host/port options, and the 'error' event — Node.js (2026)
  3. connect(2) — Linux man page: ECONNREFUSED is returned when no one is listening on the remote address — Linux man-pages (2026)
  4. Port — MDN Web Docs glossary: what a network port is and how it identifies a listening service on a host — MDN Web Docs (2026)
Share:
Hrishikesh Baidya
Hrishikesh Baidya· Chief Technology Officer

Hrishikesh Baidya is the CTO at Softech Infra. He is drawn to architecture that is invisible — systems that simply work — and leads the engineering behind BugMojo.

On this page

  • What ECONNREFUSED actually means
  • ECONNREFUSED vs ETIMEDOUT vs ENOTFOUND
  • The six causes (and their fixes)
  • How to diagnose it step by step
  • Related failure modes

Get bug-tracking insights, weekly.

Engineering deep-dives, QA playbooks, and honest tool comparisons. No spam — unsubscribe in one click.