# “connect ECONNREFUSED” (and ERR_CONNECTION_REFUSED)

> connect ECONNREFUSED 127.0.0.1 or ::1. Start the service, use the right host and port, and use the service name instead of localhost in Docker.

Canonical: https://app369.com/fix/econnrefused-connection-refused/
Last updated: 2026-11-07

A Node.js app fails with Error: connect ECONNREFUSED 127.0.0.1:5432 (or ::1:3000, or an AggregateError listing both), or the browser shows ERR_CONNECTION_REFUSED. The address was reachable, but nothing was listening on that port, so the machine refused the connection. The address and port in the message tell you exactly what the code tried to reach.

**Short answer**

Read the address and port in the error, then check something is running there: the database, Redis, your API or the dev server. If it is running, the host is wrong for where the code runs: inside Docker, localhost is the container itself, so use the service name from docker-compose (db:5432) or host.docker.internal. On Node.js 17 or 18, an error showing ::1 can mean localhost resolved to IPv6 while the server listens on IPv4 only: use 127.0.0.1 or make the server listen on both. On a host like Vercel, localhost never works: use the real database URL.

## **Why the connection is refused, and the fix**

1. 01 ### **The service is not running**
  > “This usually results from trying to connect to a service that is inactive on the foreign host.” [**Node.js, Errors: Common system errors**](https://nodejs.org/api/errors.html) The most common case: the database, cache or API the app connects to was never started, crashed, or listens on a different port. Start it, and check the port it actually uses.```
# is anything listening on 5432?
lsof -nP -iTCP:5432 -sTCP:LISTEN   # macOS / Linux

# start it, for example with Docker
docker compose up -d db
```
2. 02 ### **localhost inside Docker points at the container** In a container, localhost and 127.0.0.1 mean the container itself, not your machine or the other containers. Use the compose service name as the host, or host.docker.internal to reach a service on your machine (on Linux, add it with extra_hosts). Two more container cases: the app can start before the database is ready, so wait for a healthcheck (below) or retry; and a server inside a container that binds to 127.0.0.1 can’t be reached through a published port, so bind it to 0.0.0.0.```
# docker-compose.yml
services:
  api:
    environment:
      DATABASE_URL: postgres://app:secret@db:5432/app
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: app
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 2s
      retries: 15
```
3. 03 ### **::1 versus 127.0.0.1 (Node.js 17 and 18)**
  > “v17.0.0 Changed default value to verbatim.” [**Node.js, DNS: dns.setDefaultResultOrder()**](https://nodejs.org/api/dns.html) Since Node.js 17, localhost is resolved in the order the system returns, so it can resolve to the IPv6 address ::1 first. On Node.js 17 and 18, a server that only listens on IPv4 refuses that connection. Node.js 20 and later try the next address automatically, so there an error that lists both ::1 and 127.0.0.1 (AggregateError) means nothing is listening on that port at all. Either way, use 127.0.0.1 in the connection string, or make the server listen on both families.
4. 04 ### **Production still points at localhost** A connection string copied from .env.local into production points at a database that only existed on your laptop. On Vercel, Netlify or a container host, set the real host in the environment variables, and make sure the database allows connections from that host.

Checked against the vendor’s docs on November 7, 2026: [Node.js: Errors: Common system errors (ECONNREFUSED)](https://nodejs.org/api/errors.html); [Node.js: DNS (result order, IPv4 and IPv6)](https://nodejs.org/api/dns.html).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**Node.js EADDRINUSE: address already in use**](https://app369.com/fix/node-eaddrinuse-address-already-in-use/) · [**Prisma “Can’t reach database server”**](https://app369.com/fix/supabase-prisma-cant-reach-database/) · [**“Uncaught (in promise)”**](https://app369.com/fix/uncaught-in-promise/)

Questions

## **More on this error**

<details>

<summary>**What is the difference between ECONNREFUSED and a timeout? **+</summary>

Refused means the machine answered and said nothing is listening on that port, so the fix is to start the service or change the port. A timeout (ETIMEDOUT) means nothing answered at all, which points at a firewall, a wrong IP address or a network rule.

</details>

<details>

<summary>**Why does it work in the browser but not from my server code? **+</summary>

They run in different places. The browser runs on your machine; the server code may run in Docker, a VM or a cloud host, where localhost is somewhere else. Use an address that is correct from where the server code runs.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)