Установка PostgreSQL 16 занимает минут десять, а разгребать последствия поспешной настройки порой приходится годами. Сервер ставится почти на любом дистрибутиве Linux без сюрпризов, стартует с первой попытки и смирно слушает локальный сокет, из-за чего у инженера быстро рождается ощущение завершённой работы. Версия 16, выпущенная 14 сентября 2023 года, стала заметно строже к паролям в файле pg_hba.conf, однако умолчания не спасают от невнимательности: одна забытая строка со словом trust превращает строгие настройки в декорацию. Путь от чистого сервера до базы, куда приложение ходит по сети под отдельным именем с ограниченными правами, короткий, но каждый шаг в нём либо закрывает дверь, либо распахивает её настежь.

Подготовка сервера Linux перед установкой PostgreSQL 16

Первым делом смотрят, какая версия лежит в штатном репозитории дистрибутива. В Ubuntu 24.04 PostgreSQL 16 входит в стандартный набор пакетов, в Debian 12 штатной остаётся версия 15, поэтому 16-ю приносят из официального репозитория проекта. В RHEL 9.4 и его клонах нужная версия доступна как поток модуля dnf. Решение, откуда ставить, принимают до установки: смешивание пакетов из двух репозиториев оборачивается рассинхроном обновлений.

Кроме версии, серверу нужен запас места на разделе с данными и корректная локаль. Кластер инициализируется с той локалью, которую видит утилита initdb, и поменять её позже без выгрузки и восстановления не выйдет. Проверка перед установкой занимает меньше минуты:

cat /etc/os-release
free -h
df -h /var/lib
localectl status

Если localectl показывает что-то вроде ru_RU.UTF-8, сортировка строк работает так, как ждёт приложение. Для сервера с данными на отдельном разделе заранее сверяют точку монтирования с будущим путём кластера: у Debian-семейства это /var/lib/postgresql, у Red Hat-семейства /var/lib/pgsql.

Установка PostgreSQL 16 в Debian, Ubuntu и сборках семейства Red Hat

В Ubuntu 24.04 всё сводится к двум командам:

sudo apt update
sudo apt install -y postgresql-16

В Debian 12 сначала подключают официальный репозиторий проекта и только потом ставят пакет 16-й версии:

sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-16

Установщик Debian-семейства сам инициализирует кластер с именем main, раскладывает конфигурацию по /etc/postgresql/16/main, данные в /var/lib/postgresql/16/main, журнал в /var/log/postgresql. Служба получает имя postgresql@16-main, ею управляют через systemctl, а утилита pg_ctlcluster берёт на себя детали.

В RHEL, AlmaLinux и Rocky версия живёт в потоке модуля:

sudo dnf module list postgresql
sudo dnf module enable postgresql:16
sudo dnf install -y postgresql-server
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql

Инициализацию кластера здесь выполняют вручную, её пропуск оборачивается службой, которая стартует без данных и падает с невнятной ошибкой. Служба называется postgresql, данные обычно лежат в /var/lib/pgsql/data. У сборок с номером версии в имени пакета, например postgresql16-server, служба называется postgresql-16, а данные в /var/lib/pgsql/16/data. Разница в путях бьёт по скриптам мониторинга, поэтому путь сверяют запросом, а не по памяти.

Первый вход под postgres и проверка живости нового кластера

После установки в кластере ровно одна роль, postgres, суперпользователь без пароля. Войти под ним с той же машины позволяет метод peer: подключение идёт через локальный сокет, сервер сверяет имя системного пользователя с именем роли, и пароль не запрашивается вовсе. Базовые проверки выглядят так:

sudo -u postgres psql -c "SELECT version();"
sudo -u postgres psql -c "SHOW data_directory;"
sudo -u postgres pg_lsclusters
pg_isready -h 127.0.0.1 -p 5432

Команда pg_lsclusters существует только в Debian-семействе, в системах Red Hat вместо неё смотрят состояние службы и её журнал. Внутри сессии psql пригодятся три команды: \l выводит список баз, \du показывает роли с атрибутами, \conninfo напоминает параметры текущего подключения. Задавать пароль суперпользователю при работе через peer не требуется: по сети вход postgres всё равно запрещён, пока не разрешён явно.

Создание ролей и баз без прав суперпользователя

Пользователь и роль внутри PostgreSQL давно означают одно и то же, различие сводится к атрибуту LOGIN. Для приложения разумная схема включает минимум три роли: владелец схемы без права входа, рабочая роль с паролем и, если нужны отчёты, читатель с правами только на чтение. Приложение под суперпользователем похоже на мину замедленного действия: одна SQL-инъекция в коде, и злоумышленник получает право стирать базы, создавать роли и читать чужие таблицы.

Безопасный порядок создания рабочей базы выглядит так:

  1. создаётся роль владельца без права входа (NOLOGIN);
  2. появляется рабочая роль с паролем и лимитом соединений;
  3. создаётся база, владельцем которой назначена первая роль;
  4. рабочей роли выдаются права только на нужную схему;
  5. вход под новым именем проверяют с клиентской машины.

В SQL перечисленное складывается в несколько коротких команд:

CREATE ROLE app_owner NOLOGIN;
CREATE ROLE app_user LOGIN PASSWORD 'D3nse_Passphrase_2024' CONNECTION LIMIT 20;
CREATE DATABASE appdb OWNER app_owner ENCODING 'UTF8';

Права рабочей роли и читателя выдают явно и по минимуму:

GRANT CONNECT ON DATABASE appdb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;

CREATE ROLE reader LOGIN PASSWORD 'An0ther_Passphrase' CONNECTION LIMIT 5;
GRANT CONNECT ON DATABASE appdb TO reader;
GRANT USAGE ON SCHEMA public TO reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO reader;
ALTER DEFAULT PRIVILEGES FOR ROLE app_owner IN SCHEMA public
  GRANT SELECT ON TABLES TO reader;

Команда ALTER DEFAULT PRIVILEGES закрывает типичную дыру: без неё новые таблицы читателю видны не будут, и заявка о пропавшем доступе придёт через месяц. Ролям назначают и персональные лимиты, они срабатывают при каждом входе:

ALTER ROLE app_user SET statement_timeout = '30s';
ALTER ROLE app_user SET idle_in_transaction_session_timeout = '60s';
ALTER ROLE reader SET default_transaction_read_only = on;

Быстрая проверка атрибутов ролей:

SELECT rolname, rolsuper, rolcanlogin, rolconnlimit FROM pg_roles;

Отдельная засада ждёт тех, кто переносил привычки со старых версий: начиная с 15-й, схема public по умолчанию не разрешает создавать объекты всем подряд. Ошибка "permission denied for schema public" для рабочей роли означает не поломку, а новое умолчание в работе.

Правила pg_hba.conf для локального и сетевого доступа

Файл pg_hba.conf отвечает на три вопроса: кого пускать, откуда и по какому механизму проверки. Строка состоит из пяти колонок: тип подключения, база, роль, адрес, метод. Строки просматриваются сверху вниз, срабатывает первое подходящее правило, остальное сервер уже не читает. Порядок здесь важнее содержания: широкая строка, оказавшаяся выше узкой, тихо отменяет узкую, и конфигурация работает совсем не так, как кажется администратору.

Кластер, созданный версией 16, получает в сгенерированном файле метод scram-sha-256 вместо устаревшего md5. Рабочий пример с сетевым доступом для приложения выглядит так:

local   all         postgres                  peer
host    appdb       app_user   192.168.10.0/24  scram-sha-256
host    appdb       reader     192.168.10.0/24  scram-sha-256
host    all         all        127.0.0.1/32    scram-sha-256
host    all         all        ::1/128         scram-sha-256
host    all         all        0.0.0.0/0       reject
host    all         all        ::/0            reject

Метод peer работает только на локальном сокете и сверяет системного пользователя с ролью. Метод scram-sha-256 передаёт по сети не пароль, а доказательство его знания; восстановить пароль из перехваченного трафика заметно сложнее. Метод trust пароль не проверяет вовсе, его место в аварийном восстановлении, а не в рабочем файле. Финальные строки с reject закрывают все прочие адреса честным отказом вместо молчаливого игнора: разбирать жалобу "не подключается" проще, когда сервер прямо говорит "нет".

Для диагностики отказов включают журналирование попыток входа:

log_connections = on

Тогда в журнале появляется запись о каждой попытке, и спор о том, какая строка кого не пустила, заканчивается за минуту.

Сетевые параметры postgresql.conf и перезагрузка без остановки базы

Свежеустановленный сервер слушает только localhost: параметр listen_addresses ограничен локальными адресами. Для сетевого доступа параметр меняют на конкретный адрес или список адресов, порт при необходимости тоже. Эти настройки читает постмастер при старте, поэтому для них нужен полный перезапуск службы. Изменения в pg_hba.conf и большинство параметров postgresql.conf вступают в силу после перезагрузки конфигурации, без разрыва рабочих соединений:

listen_addresses = '192.168.10.5'
port = 5432
log_connections = on
password_encryption = scram-sha-256

Перечитывание и перезапуск выполняют средствами дистрибутива:

sudo systemctl reload postgresql@16-main
sudo systemctl restart postgresql@16-main

Первая команда достаточна для правок pg_hba.conf, вторая нужна для listen_addresses и port. В Red Hat-семействе имя службы postgresql или postgresql-16, логика та же.

Сетевому доступу обычно мешает ещё и межсетевой экран. В Ubuntu с ufw хватает одного правила:

sudo ufw allow from 192.168.10.0/24 to any port 5432 proto tcp

В системах с firewalld для клиентов базы создают отдельную зону:

sudo firewall-cmd --permanent --new-zone=pgclients
sudo firewall-cmd --permanent --zone=pgclients --add-source=192.168.10.0/24
sudo firewall-cmd --permanent --zone=pgclients --add-port=5432/tcp
sudo firewall-cmd --reload

Проверку выполняют с клиентской машины, а не с самого сервера, иначе проверяется не то:

psql "host=192.168.10.5 port=5432 dbname=appdb user=app_user"
pg_isready -h 192.168.10.5 -p 5432

Утилита pg_isready сообщает лишь, что порт отвечает, аутентификацию она не проверяет, отсюда частые ложные выводы о работоспособности. Внутри psql команда \conninfo подтверждает, что подключение действительно прошло по сети, а не через сокет. Для шифрования трафика у сборок Debian-семейства параметр ssl = on уже включён, поэтому клиентские драйверы стоит перевести на sslmode=require.

Резервная копия ролей и ошибки, которые оставляют базу открытой

Роли хранятся на уровне кластера, а не отдельной базы, поэтому обычный pg_dump одной базы их не сохраняет. Для резервной копии ролей, хешей паролей и лимитов существует отдельный режим:

sudo -u postgres pg_dumpall --roles-only > roles_$(date +%F).sql
chmod 600 roles_*.sql

В получившемся файле лежат хеши паролей, поэтому хранить его в общедоступном каталоге равносильно оставленной в замке связке ключей. Рядом держат копии pg_hba.conf и postgresql.conf, такая копия экономит вечер восстановления по памяти.

Список типичных ошибок устойчив от версии к версии. Правки в pg_hba.conf без перечитывания конфигурации: файл меняют, службу не перезагружают, полчаса ищут причину отказа. Широкая строка над узкой: правило для all в начале файла отменяет всё, что написано ниже. Приложение под суперпользователем, чтобы не разбираться с правами: удобство до первой инъекции. Строка trust, добавленная на время тестов и забытая в рабочем файле: самый быстрый способ отдать базу любому, кто найдёт порт. Пара из параметра listen_addresses, открытого на все адреса, и правила scram-sha-256 на весь интернет: база становится видна миру.

Хорошая новость в том, что перечисленные ошибки видны заранее. Запрос к pg_stat_activity показывает, кто подключен прямо сейчас и что выполняет:

SELECT pid, usename, datname, state, query FROM pg_stat_activity;

Журнал входов превращает догадки в факты, а представление pg_stat_io, появившееся в 16-й версии, раскрывает физику ввода-вывода. База, к которой не может подключиться никто, безопасна, но бесполезна, а база, открытая всем, полезна, но уже не своя. Пока каждая строка pg_hba.conf отвечает на три вопроса однозначно, PostgreSQL 16 на Linux стоит надёжно и не создаёт хозяину лишних сюрпризов. Впрочем, сюрпризы всё равно придут: однажды на сервере появится вторая база, третий разработчик и просьба открыть доступ "на минутку", и тогда правильные привычки окажутся дороже любых инструкций.