1. Установка PostgreSQL 16 из официального репозитория
Ubuntu 22.04 по умолчанию предлагает PostgreSQL 14 — устаревшую версию. Устанавливаем из официального PGDG-репозитория, где всегда актуальные версии.
Требования к VPS
Минимум 1 ГБ RAM. Для комфортной работы — 2 ГБ и более. Диск — NVMe/SSD обязателен для хорошей производительности. Виртуализация — KVM или любая другая (PostgreSQL не требует Docker).
# Установить зависимости
apt install -y curl ca-certificates
# Создать директорию для ключей
install -d /usr/share/postgresql-common/pgdg
# Скачать ключ репозитория
curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail \
https://www.postgresql.org/media/keys/ACCC4CF8.asc
# Добавить репозиторий
sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
> /etc/apt/sources.list.d/pgdg.list'apt update
apt install -y postgresql-16 postgresql-client-16
# Проверить статус
systemctl status postgresql@16-mainПосле установки PostgreSQL уже запущен и включён в автозапуск. Данные хранятся в /var/lib/postgresql/16/main/, конфиги — в /etc/postgresql/16/main/.
2. Первые шаги: подключение и смена пароля
После установки создаётся системный пользователь postgres и одноимённая суперпользовательская роль в PostgreSQL. Пароля по умолчанию нет — подключение только через sudo -u postgres psql.
# Войти в psql от имени postgres
sudo -u postgres psql
# Внутри psql — установить пароль:
ALTER USER postgres WITH PASSWORD 'СЛОЖНЫЙ_ПАРОЛЬ_ЗДЕСЬ';
# Выйти
\qsudo -u postgres psql
\l -- список всех баз данных
\du -- список пользователей и ролей
\c mydb -- переключиться на базу mydb
\dt -- список таблиц в текущей базе
\d users -- структура таблицы users
\q -- выходМетод аутентификации peer
По умолчанию подключение через sudo -u postgres psql использует метод peer — PostgreSQL доверяет системному пользователю без пароля. Это удобно для локального управления. Для приложений и удалённых клиентов используется метод scram-sha-256 с паролем.
3. Создание пользователей и баз данных
Хорошая практика — каждое приложение работает со своим пользователем и своей базой. Никогда не подключайте приложения под суперпользователем postgres.
Создать пользователя и базу
# Создать пользователя с паролем
sudo -u postgres createuser --pwprompt appuser
# Создать базу данных, владелец — appuser
sudo -u postgres createdb --owner=appuser myapp_dbsudo -u postgres psql
-- Создать пользователя
CREATE USER appuser WITH PASSWORD 'ПАРОЛЬ';
-- Создать базу данных
CREATE DATABASE myapp_db OWNER appuser ENCODING 'UTF8' LC_COLLATE 'ru_RU.UTF-8' LC_CTYPE 'ru_RU.UTF-8' TEMPLATE template0;
-- Выдать права (нужно если база создана без --owner)
GRANT ALL PRIVILEGES ON DATABASE myapp_db TO appuser;PostgreSQL 15+: изменения в правах на схему public
Начиная с PostgreSQL 15 право CREATE в схеме public по умолчанию отозвано у всех кроме суперпользователя. После создания базы выполните:
sudo -u postgres psql -d myapp_db
GRANT ALL ON SCHEMA public TO appuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO appuser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO appuser;Проверить подключение
psql -h localhost -U appuser -d myapp_db
# При запросе пароля — введите пароль, заданный при создании пользователяConnection string для приложений
# .env или конфиг приложения DATABASE_URL=postgresql://appuser:ПАРОЛЬ@localhost:5432/myapp_db # Python (psycopg2/SQLAlchemy) postgresql+psycopg2://appuser:ПАРОЛЬ@localhost/myapp_db # Node.js (pg/Prisma) postgresql://appuser:ПАРОЛЬ@localhost:5432/myapp_db
4. Оптимизация postgresql.conf под ваш VPS
Дефолтные настройки PostgreSQL рассчитаны на сервер с 1 ГБ RAM. Подберём параметры под реальный объём памяти вашего VPS. Конфиг: /etc/postgresql/16/main/postgresql.conf.
| Параметр | 1 ГБ RAM | 2 ГБ RAM | 4 ГБ RAM | Что делает |
|---|---|---|---|---|
| shared_buffers | 256MB | 512MB | 1GB | Кэш данных PostgreSQL. 25% RAM. |
| work_mem | 4MB | 8MB | 16MB | Память на сортировку/хэш на операцию. Умножать на max_connections. |
| maintenance_work_mem | 64MB | 128MB | 256MB | VACUUM, CREATE INDEX. Можно побольше. |
| effective_cache_size | 512MB | 1GB | 2GB | Подсказка планировщику: сколько данных в кэше ОС + PG. |
| wal_buffers | 8MB | 16MB | 16MB | Буфер WAL. 1/32 от shared_buffers, но не менее 8 МБ. |
| max_connections | 100 | 100 | 150 | Максимум соединений. Используйте пул в приложении. |
| checkpoint_completion_target | 0.9 | 0.9 | 0.9 | Размазывает запись чекпоинта. 0.9 — всегда. |
Пример конфига для VPS с 2 ГБ RAM
# ── Память ───────────────────────────────────────────────
shared_buffers = 512MB # 25% от 2 ГБ
work_mem = 8MB # на каждую операцию сортировки
maintenance_work_mem = 128MB # VACUUM, CREATE INDEX
effective_cache_size = 1GB # ~50% RAM
# ── Соединения ───────────────────────────────────────────
max_connections = 100
# ── WAL и чекпоинты ──────────────────────────────────────
wal_buffers = 16MB
checkpoint_completion_target = 0.9
# ── Логирование медленных запросов (рекомендуется) ───────
log_min_duration_statement = 1000 # запросы дольше 1 секунды
log_line_prefix = '%t [%p] %u@%d ' # метаданные в логе# Проверить синтаксис конфига
sudo -u postgres pg_ctlcluster 16 main status
# Перезапустить PostgreSQL (нужно при изменении shared_buffers)
systemctl restart postgresql@16-main
# Или перечитать конфиг без рестарта (для большинства параметров)
sudo -u postgres psql -c "SELECT pg_reload_conf();"
# Проверить, что параметры применились
sudo -u postgres psql -c "SHOW shared_buffers; SHOW work_mem;"Автоматический расчёт параметров
Онлайн-калькулятор pgtune.leopard.in.ua генерирует postgresql.conf под вашу конфигурацию: укажите RAM, число CPU, тип диска (SSD/HDD) и тип нагрузки (OLTP, DWH, Web). Хорошая отправная точка.
5. Удалённый доступ: pg_hba.conf и UFW
По умолчанию PostgreSQL слушает только локальный сокет. Если приложение на другом сервере или нужно подключиться из IDE — настроим удалённый доступ безопасно.
Шаг 1: изменить listen_addresses
# Слушать на всех интерфейсах (или укажите конкретный IP)
listen_addresses = '*'Шаг 2: настроить pg_hba.conf
Файл /etc/postgresql/16/main/pg_hba.conf контролирует, кто и как может подключиться. Правила проверяются сверху вниз.
# Формат: TYPE DATABASE USER ADDRESS METHOD
# Локальные соединения (без изменений):
local all postgres peer
local all all md5
# Удалённый доступ — только с конкретного IP приложения:
host myapp_db appuser 1.2.3.4/32 scram-sha-256
# Если нужен доступ из подсети (например, все серверы в 10.0.0.0/24):
# host myapp_db appuser 10.0.0.0/24 scram-sha-256Шаг 3: открыть порт в UFW
# Только для IP приложения:
ufw allow from 1.2.3.4 to any port 5432
# Проверить правила:
ufw status
# Перезапустить PostgreSQL:
systemctl restart postgresql@16-mainscram-sha-256РекомендуетсяСовременный метод с хэшированием пароля. Используйте его по умолчанию.
md5УстарелMD5-хэш пароля. Поддерживается для совместимости, но слабее scram-sha-256.
trustТолько localhostБез пароля. Допустимо только для локального сокета. Никогда для удалённых.
6. Резервные копии: pg_dump и автобэкап
PostgreSQL поставляется с утилитами pg_dump и pg_dumpall — больше ничего устанавливать не нужно.
Разовый бэкап базы
# Бэкап одной базы (рекомендуемый формат -Fc — сжатый, поддерживает выборочный restore)
sudo -u postgres pg_dump -Fc myapp_db -f /backup/myapp_db_$(date +%Y%m%d).dump
# Бэкап в читаемом SQL (удобен для переноса данных)
sudo -u postgres pg_dump myapp_db > /backup/myapp_db_$(date +%Y%m%d).sql# Восстановить в существующую (пустую) базу
pg_restore -h localhost -U postgres -d myapp_db /backup/myapp_db_20260305.dump
# Восстановить из SQL-дампа
psql -U postgres -d myapp_db -f /backup/myapp_db_20260305.sqlАвтоматический бэкап через cron
#!/bin/bash
BACKUP_DIR="/backup/postgres"
KEEP_DAYS=14 # хранить 14 дней
mkdir -p "$BACKUP_DIR"
# Бэкап всех баз по отдельности
for DB in $(sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres'"); do
sudo -u postgres pg_dump -Fc "$DB" -f "$BACKUP_DIR/${DB}_$(date +%Y%m%d_%H%M).dump"
echo "$(date): Backed up $DB"
done
# Удалить бэкапы старше KEEP_DAYS дней
find "$BACKUP_DIR" -name "*.dump" -mtime +$KEEP_DAYS -deletechmod +x /usr/local/bin/pg_backup.sh
# Добавить в crontab (запуск каждый день в 3:00)
crontab -e
# Добавить строку:
0 3 * * * /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1# Полный бэкап кластера — нужен при миграции сервера
sudo -u postgres pg_dumpall -f /backup/cluster_$(date +%Y%m%d).sql
# Только роли и пользователи (без данных баз)
sudo -u postgres pg_dumpall --roles-only -f /backup/roles_$(date +%Y%m%d).sqlПроверяйте бэкапы
Нечитаемый бэкап хуже его отсутствия. Раз в неделю или месяц восстанавливайте дамп в тестовую базу и убеждайтесь, что данные там:
sudo -u postgres createdb myapp_db_test pg_restore -U postgres -d myapp_db_test /backup/myapp_db_latest.dump sudo -u postgres psql -d myapp_db_test -c "SELECT COUNT(*) FROM your_main_table;"
7. Анализ запросов: pg_stat_statements
pg_stat_statements — встроенное расширение, которое собирает статистику по всем запросам: время выполнения, количество вызовов, операции с диском. Это первое, что нужно включить на продакшен-сервере.
Установка и активация
apt install -y postgresql-contrib-16shared_preload_libraries = 'pg_stat_statements'
# Дополнительные параметры (опционально):
pg_stat_statements.max = 10000 # хранить топ-10000 запросов
pg_stat_statements.track = all # отслеживать вложенные запросы тожеsystemctl restart postgresql@16-main
# Создать расширение в нужной базе (нужно для каждой базы отдельно)
sudo -u postgres psql -d myapp_db -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"Полезные запросы
sudo -u postgres psql -d myapp_db -c "
SELECT
round(mean_exec_time::numeric, 2) AS avg_ms,
calls,
round(total_exec_time::numeric, 2) AS total_ms,
left(query, 80) AS query
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
"sudo -u postgres psql -d myapp_db -c "
SELECT
round(total_exec_time::numeric, 2) AS total_ms,
calls,
round(mean_exec_time::numeric, 2) AS avg_ms,
left(query, 80) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
"sudo -u postgres psql -d myapp_db -c "
SELECT
shared_blks_read AS disk_reads,
shared_blks_hit AS cache_hits,
calls,
left(query, 80) AS query
FROM pg_stat_statements
WHERE shared_blks_read > 1000
ORDER BY shared_blks_read DESC
LIMIT 10;
"sudo -u postgres psql -d myapp_db -c "SELECT pg_stat_statements_reset();"Что делать с медленными запросами
- 1Скопируйте медленный запрос и выполните
EXPLAIN ANALYZE ваш_запрос— увидите план выполнения - 2Если в плане есть Seq Scan на большой таблице — нужен индекс
- 3Создайте индекс:
CREATE INDEX CONCURRENTLY idx_users_email ON users(email); - 4CONCURRENTLY создаёт индекс без блокировки таблицы — используйте его на проде
8. Шпаргалка psql-команд
| Задача | Команда |
|---|---|
| Войти в psql | sudo -u postgres psql |
| Войти в конкретную базу | sudo -u postgres psql -d myapp_db |
| Список баз | \l |
| Список пользователей | \du |
| Список таблиц | \dt |
| Переключить базу | \c myapp_db |
| Структура таблицы | \d users |
| Размер баз данных | SELECT pg_size_pretty(pg_database_size(datname)), datname FROM pg_database ORDER BY 1 DESC; |
| Размер таблиц | SELECT pg_size_pretty(pg_total_relation_size(relname::text)), relname FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relname::text) DESC; |
| Активные соединения | SELECT count(*), state FROM pg_stat_activity GROUP BY state; |
| Версия PostgreSQL | SELECT version(); |
| Перезагрузить конфиг | SELECT pg_reload_conf(); |
| Выйти из psql | \q |
# Статус
systemctl status postgresql@16-main
# Запустить / остановить / перезапустить
systemctl start postgresql@16-main
systemctl stop postgresql@16-main
systemctl restart postgresql@16-main
# Перечитать конфиг без перезапуска
systemctl reload postgresql@16-main
# Лог ошибок (последние 50 строк)
tail -50 /var/log/postgresql/postgresql-16-main.log