// заметки
PostgreSQL: connection refused — не подключается
Connection refused и no pg_hba.conf entry выглядят похоже, но лежат на разных уровнях. Разбираю диагностику, причины и фиксы для локального PostgreSQL и Docker
PostgreSQL отказывает в подключении: psql отвечает Connection refused или FATAL: no pg_hba.conf entry. Эти две ошибки лежат на разных уровнях, и лечить их нужно по-разному. Ниже — как за пару команд понять, в чём дело: сервис не запущен, слушает не тот адрес, занят порт, не опубликован порт в Docker или соединение отклоняет pg_hba.conf. Для каждой причины — свой фикс
Симптом
Клиент падает на сетевом уровне, когда до порта никто не слушает
$ psql -h 127.0.0.1 -p 5432 -U app -d appdb
psql: error: connection to server at "127.0.0.1", port 5432 failed: Connection refused
Is the server running on that host and accepting TCP/IP connections?Другая ошибка означает, что сервер ответил, но закрыл соединение по своим правилам
$ psql "postgresql://app@db.example.com:5432/appdb"
psql: error: connection to server at "db.example.com" (203.0.113.10), port 5432 failed:
FATAL: no pg_hba.conf entry for host "203.0.113.10", user "app", database "appdb", no encryptionПервая ошибка — сетевой уровень: пакет пришёл, слушателя нет. Вторая — уровень аутентификации: сервер доступен, но pg_hba.conf не пропускает конкретного пользователя. Перепутать их дорого: правка pg_hba.conf не поднимет остановленный процесс, а перезапуск сервиса не отменит правила аутентификации
Диагностика
Первая команда — pg_isready. Она стучится в порт и возвращает код выхода: 0 — принимает соединения, 1 — отклоняет, 2 — не отвечает, 3 — попытка не сделана (неверные параметры подключения). Если утилита не найдена, смотрите разбор Linux: command not found
$ pg_isready -h 127.0.0.1 -p 5432
127.0.0.1:5432 - accepting connections
$ echo $?
0Дальше смотрим, кто и на каком адресе слушает порт
$ ss -lntp | grep 5432
LISTEN 0 244 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=1234,fd=6))127.0.0.1:5432 — только loopback, 0.0.0.0:5432 — все интерфейсы, пустой вывод — не слушает никто. Unix-сокет виден отдельно
$ ss -lxnp | grep PGSQL
u_str LISTEN 0 244 /var/run/postgresql/.s.PGSQL.5432 12345 * 0 users:(("postgres",pid=1234,fd=6))Состояние сервиса зависит от способа установки. В Debian и Ubuntu мета-юнит postgresql.service может быть active (exited), пока реальный кластер лежит, поэтому проверяем сам кластер
$ systemctl status postgresql --no-pager
● postgresql.service - PostgreSQL RDBMS
Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; preset: enabled)
Active: active (exited) since ...
$ pg_lsclusters
Ver Cluster Port Status Owner Data directory Log file
16 main 5432 down postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.logДля сборок без systemd статус показывает pg_ctl
$ pg_ctl status -D "$PGDATA"
pg_ctl: server is running (PID: 1234)В Docker проверяем публикацию порта и логи. Пустая колонка PORTS означает, что порт не опубликован
$ docker ps
CONTAINER ID IMAGE ... PORTS NAMES
a1b2c3d4e5f6 postgres:16 ... 0.0.0.0:5432->5432/tcp db
$ docker logs --tail 50 db
PostgreSQL init process complete; ready for start up.
LOG: listening on IPv4 address "0.0.0.0", port 5432
LOG: database system is ready to accept connectionsЕсли изнутри контейнера сервер принимает соединения, а снаружи Connection refused, проблема в публикации порта, а не в PostgreSQL
$ docker exec -it db pg_isready
/var/run/postgresql:5432 - accepting connectionsПричина
Причин несколько, и каждая проверяется отдельно
- Процесс не запущен или упал на старте: занят порт, недоступен
PGDATA, ошибка в конфиге, нет прав на каталог данных listen_addresses = 'localhost'вpostgresql.conf: сервер слушает только127.0.0.1и не принимает внешние подключения- Порт занят или не тот: другой кластер или версия слушает 5432, а клиент идёт на 5433
- В Docker порт не опубликован: контейнер запущен без
-p 5432:5432либо публикация привязана только к127.0.0.1 - Из контейнера обращаются к
localhost: там нет хостового PostgreSQL, нужен адрес сервиса или хост-шлюз - Firewall режет пакеты: обычно это timeout, а не refusal, но проверить стоит
pg_hba.confне содержит подходящей строки для адреса, пользователя или базы
Connection refused— это TCP RST от ядра: пакет дошёл до хоста, но на порту никто не слушает. Firewall чаще дропает пакет, и клиент видит timeout. Если ошибка именноConnection refused, виновата не фильтрация, а отсутствие слушателя
Отдельно про pg_hba.conf. Это правила: тип подключения, база, пользователь, адрес и метод аутентификации. Если ни одна строка не подошла, сервер отвечает FATAL: no pg_hba.conf entry. Порядок важен: срабатывает первое совпадение
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host appdb app 127.0.0.1/32 scram-sha-256
host appdb app 10.0.0.0/8 scram-sha-256
host all all ::1/128 scram-sha-256local — Unix-сокет, host — TCP, hostssl — только TLS. peer сверяет имя пользователя ОС, scram-sha-256 требует пароль. Строки читаются сверху вниз, побеждает первая подходящая
Решение
Разберём по причинам
Не запущен сервис
Запускаем кластер и смотрим журнал. Подставьте своё имя кластера
$ sudo systemctl start postgresql@16-main
$ systemctl status postgresql@16-main --no-pager
$ journalctl -u postgresql@16-main -n 50 --no-pagerЕсли старт падает, причина в логе. Каталог данных должен принадлежать пользователю postgres и иметь права 700
$ sudo ls -ld /var/lib/postgresql/16/main
drwx------ 3 postgres postgres 4096 ... /var/lib/postgresql/16/mainСервер слушает только localhost
Правим listen_addresses в postgresql.conf. Параметр читается при старте, поэтому нужен restart, а не reload
# /etc/postgresql/16/main/postgresql.conf
listen_addresses = '*'$ sudo systemctl restart postgresql@16-mainПосле рестарта ss -lntp покажет 0.0.0.0:5432 или [::]:5432. Открывать сервер в интернет без firewall и сильного пароля нельзя
Порт занят или другой
Какой порт слушает сам сервер, видно в psql
$ psql -h 127.0.0.1 -p 5432 -d postgres -c 'show port;'
port
------
5432Кто занимает порт и куда ходит клиент, проверяем так
$ sudo ss -lntp 'sport = :5432'
$ sudo lsof -iTCP:5432 -sTCP:LISTENЕсли база на 5433, укажите порт явно: psql -p 5433. Строку подключения в приложении правьте там же, где задан хост
В Docker не опубликован порт
Порт публикуется при создании контейнера, на лету его не меняют
$ docker run -d --name db -p 127.0.0.1:5432:5432 \
-e POSTGRES_PASSWORD='<password>' postgres:16В Compose то же самое описывает секция ports
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"
environment:
POSTGRES_PASSWORD: "<password>"-p 127.0.0.1:5432:5432 публикует порт только на loopback хоста, -p 5432:5432 — на все интерфейсы. Для локальной разработки хватит первого
Подключение к localhost из контейнера
Внутри контейнера localhost указывает на сам контейнер. Если база в соседнем сервисе Compose, используйте его имя
# вместо postgresql://app@localhost:5432/appdb
postgresql://app@db:5432/appdbЕсли база на хосте, в Linux добавьте шлюз: docker run --add-host=host.docker.internal:host-gateway. Надёжнее всё же держать базу сервисом в той же сети Compose
Firewall
Внешние подключения фильтруются на хосте и в облачной панели. Открывайте порт точечно
$ sudo ufw allow from 203.0.113.0/24 to any port 5432 proto tcp
$ sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port port="5432" protocol="tcp" accept'Помните про симптом: Connection refused — это не firewall. Сначала убедитесь, что порт слушается, и только потом трогайте фильтры
Правила pg_hba
Добавьте строку под своего пользователя и перечитайте правила. pg_hba.conf применяется без рестарта
# /etc/postgresql/16/main/pg_hba.conf
host appdb app 10.0.0.0/8 scram-sha-256$ sudo -u postgres psql -c 'select pg_reload_conf();'
pg_reload_conf
----------------
tМетод trust пропускает без пароля и годится только для локальной разработки. peer работает для Unix-сокета и сверяет имя пользователя ОС
Разбор установки и первичной настройки базы с нуля — в отдельной заметке: PostgreSQL: установка и настройка
Проверка
Проверяем по слоям: сначала порт, потом аутентификация
$ pg_isready -h 127.0.0.1 -p 5432
127.0.0.1:5432 - accepting connections
$ psql "postgresql://app@127.0.0.1:5432/appdb" -c 'select 1;'
?column?
----------
1
(1 row)Из контейнера
$ docker exec -it db pg_isready
/var/run/postgresql:5432 - accepting connectionsИ финальная проверка с явными параметрами
$ psql -h 127.0.0.1 -p 5432 -U app -d appdb -c '\conninfo'
You are connected to database "appdb" as user "app" on host "127.0.0.1" at port "5432".Если select 1 отдаёт строку, а приложение всё равно падает, дело уже не в сети: сверьте строку подключения и переменные окружения сервиса
Подводные камни
- Unix-сокет и TCP — разные каналы.
psqlбез-hидёт в/var/run/postgresql, аpsql -h localhost— по TCP. Сервер может принимать сокет и отказывать по сети: правилаlocalиhostнезависимы - `peer` для локальных подключений. В Debian и Ubuntu
local all all peerсверяет имя пользователя ОС.psql -U postgresпод обычным пользователем дастPeer authentication failed, нуженsudo -u postgres psql - `localhost` может резолвиться в IPv6. Если сервер слушает только
127.0.0.1, аlocalhostпревращается в::1, клиент получитConnection refused. Указывайте127.0.0.1явно - `PGDATA` задаёт, какой кластер читать. При ручном запуске неверный путь поднимает пустой или чужой кластер. Проверяйте:
pg_ctl status -D "$PGDATA" - Несколько кластеров на одном порту. На Debian и Ubuntu это покажет
pg_lsclusters, победит тот, кто занял порт первым - `pg_hba.conf` читается сверху вниз. Строка
host all all 0.0.0.0/0 trustвыше вашей обнулит остальные правила
Итог
Connection refused лечится проверкой слушателя: процесс не запущен, не тот адрес, не опубликован порт. no pg_hba.conf entry — это уже правила, и сервер доступен по сети. Порядок диагностики: pg_isready, затем ss -lntp, затем pg_hba.conf
Если база должна работать под нагрузкой и без сюрпризов, а не крутиться только на ноутбуке, помогут консалтинг по PostgreSQL и разработка backend
Похожие заметки
Установка и настройка PostgreSQL
Установка PostgreSQL через apt и Docker: кластер, initdb, pg_hba, доступы и параметры под нагрузку. Пошаговые команды с проверкой на каждом шаге
pkill -f убивает сам себя: шаблон совпадает с командой
pkill -f и pgrep -f сопоставляют шаблон со всей командной строкой, включая обёртку bash -c. Разбираю, почему команда убивает себя, и какие фильтры помогают
Nix на CachyOS: установка невозможна — rootless-запуск
Nix не ставится, если корень read-only, sudo сломан, а путь /nix зашит в ELF. Разбор трёх ограничений и rootless-запуск через mount-namespace и chroot