ECONNREFUSED 127.0.0.1:5432 — Postgres не отвечает, что делать
Сообщение Error: connect ECONNREFUSED 127.0.0.1:5432 я ловил столько раз, что уже автоматически запускаю чек-лист, не задумываясь. Объясню, что значит ошибка, и пройдусь по всем причинам, которые встречал на практике.
Что говорит ошибка
Твой Node-клиент пытается открыть TCP-соединение на 127.0.0.1:5432. Но операционная система ответила «connection refused» — никто не слушает этот порт. То есть Postgres-а на этом адресе нет, или он не отвечает на TCP, или firewall режет.
Нюанс: это не ошибка авторизации. Авторизация — это password authentication failed. Это не ошибка таймаута. Таймаут — это ETIMEDOUT. Refused — конкретно «никого нет дома».
Шаг 1: запущен ли Postgres вообще
На своей машине:
sudo systemctl status postgresql
# или
sudo systemctl status postgresql@16-mainНа macOS с Homebrew:
brew services list
# и при необходимости
brew services start postgresql@16В Docker:
docker ps | grep postgres
# если контейнер упал
docker logs <container>Если Postgres не запущен — запусти. Большинство «у меня всё было нормально вчера» лечится буквально brew services start.
Шаг 2: на каком порту слушает
Postgres по умолчанию на 5432, но не всегда. Проверь:
sudo ss -tnlp | grep -E '5432|5433'
# или netstat
sudo netstat -tnlp | grep -E '5432|5433'Если у тебя на машине несколько кластеров (5432 и 5433) — может быть, ты подключаешься не к тому. Особенно часто бывает на Ubuntu, где после апгрейда старый кластер остаётся слушать рядом.
Шаг 3: какой именно адрес слушает
Postgres может слушать только Unix-сокет, не TCP. Проверь postgresql.conf:
sudo grep -E 'listen_addresses|port' /etc/postgresql/16/main/postgresql.confДолжно быть что-то такое:
listen_addresses = 'localhost'
port = 5432Если listen_addresses = '' — Postgres слушает только Unix-сокеты, по TCP не доступен. Поправь и сделай systemctl restart postgresql.
Шаг 4: pg_hba.conf не блокирует ли
Если TCP-соединение приняли, но потом сразу разорвали, это уже не ECONNREFUSED, а ECONNRESET. Но если pg_hba настроен жёстко, и для localhost нет правила, Postgres вообще может не слушать на твоём интерфейсе. Проверь:
sudo cat /etc/postgresql/16/main/pg_hba.confДолжна быть строка типа:
host all all 127.0.0.1/32 md5Шаг 5: правильный ли порт в коде
Самое глупое, но самое частое. Проверь DATABASE_URL:
echo $DATABASE_URL
# или
cat .env | grep DATABASE_URLБывает, что в .env 5432, а контейнер с Postgres слушает на 5433 (потому что 5432 занят локальной установкой). Бывает, что в docker compose ты пробросил 5433:5432, и из приложения внутри контейнера надо ходить на 5432, а из хост-системы — на 5433.
Шаг 6: правильный ли host
В Docker Compose сервис называется по имени. Из контейнера приложения хост Postgres-сервиса — postgres, не localhost:
DATABASE_URL=postgres://app:app@postgres:5432/appЕсли ты пишешь localhost внутри контейнера — это localhost самого контейнера, в нём Postgres-а нет.
Шаг 7: firewall
На VPS проверь UFW и iptables:
sudo ufw status
sudo iptables -L INPUT -n | grep 5432Для localhost обычно ничего не нужно. Если ходишь снаружи на удалённый Postgres — открыть 5432 в файрволе.
Шаг 8: managed Postgres и SSL
На managed-Postgres (Yandex Managed, Selectel Cloud Database, RDS) часто требуется SSL. Без него соединение тоже может сразу отвергаться (в зависимости от настроек). В строке подключения добавь ?sslmode=require:
DATABASE_URL=postgres://app:app@host:5432/app?sslmode=requireПрибавляется к строке подключения, и драйвер pg сам поднимет TLS.
Шаг 9: connection limits
Если Postgres работает, но новых клиентов уже не принимает (упёрся в max_connections), на новый коннект ты можешь увидеть и таймаут, и иногда reset. Проверь:
SELECT count(*), state FROM pg_stat_activity GROUP BY state;Если число близко к max_connections — есть проблема: где-то соединения утекают. Лекарство — pgbouncer как пулер или строгий пулинг на стороне приложения.
Шаг 10: разный сетевой стек
На некоторых системах localhost резолвится в ::1 (IPv6), и Postgres может слушать только IPv4. Это редкая ситуация в Linux, но иногда встречается на macOS. Попробуй явно 127.0.0.1 вместо localhost в строке подключения.
Полный чек-лист на 30 секунд
systemctl status postgresql— запущен?ss -tnlp | grep 5432— слушает порт?echo $DATABASE_URL— правильный host/port?- Если Docker — host = имя сервиса, а не localhost.
- Если managed — добавлен ли
sslmode=require? - Если на проде вдруг — нет ли утечки коннектов?
Превентивные меры
Health check на старте приложения
async function waitForDb(timeout = 30_000) {
const start = Date.now();
while (Date.now() - start < timeout) {
try {
await db.execute(sql`select 1`);
return;
} catch (e) {
console.log('waiting for db...');
await new Promise(r => setTimeout(r, 1000));
}
}
throw new Error('Database is not reachable');
}На старте приложения ждёшь, пока БД не отзовётся. Полезно в Docker Compose, где приложение стартует чуть быстрее, чем postgres готов.
Connection pool с лимитом
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
idleTimeoutMillis: 30_000,
connectionTimeoutMillis: 5_000,
});connectionTimeoutMillis: 5000 ограничивает время ожидания свободного коннекта. Без него на загруженном пуле приложение может зависнуть на ECONNREFUSED-подобной ошибке надолго.
Алерты
Поставь алерт «БД недоступна» — на твоём health-check endpoint. Если возвращает 503 больше двух минут — пинг тебе.
Что НЕ делать
- Не пытайся «обернуть в try/catch и ретрай тысячу раз» как первое решение. Сначала пойми, почему БД не отвечает.
- Не открывай 5432 в интернет, если можно ходить через приватную сеть или туннель.
- Не ставь огромный
max_connections: если упёрся — это симптом, не причина.
ECONNREFUSED — частая, но безобидная ошибка, если знаешь, где смотреть. Чек-лист выше у меня всегда наготове, и обычно за минуту-две я выясняю, что не так. Чаще всего это банальный «забыл запустить контейнер» или «.env смотрит на не тот порт».