Major upgrade PostgreSQL — это переход между старшими версиями, например с 14 на 16 или с 15 на 17. В отличие от минорных обновлений, где обычно достаточно заменить пакеты и перезапустить службу, здесь меняется формат системных каталогов, появляются новые возможности планировщика, меняется поведение некоторых параметров и расширений. Поэтому простой apt upgrade, dnf upgrade или zypper update не перенесёт рабочий кластер в новую версию автоматически.
Инструмент pg_upgrade как раз создан для таких переходов. Он сравнивает старый и новый кластер, переносит системные каталоги, проверяет совместимость объектов и позволяет выполнить upgrade значительно быстрее, чем полный дамп и восстановление. Но у скорости есть цена: администратор должен заранее продумать backup, порядок остановки сервисов, поведение extensions, post-upgrade анализ через vacuumdb и план rollback.
Главная мысль: major upgrade PostgreSQL — это не команда, а регламент. Команда
pg_upgradeзанимает середину процесса, но успех обычно решают подготовка и проверка отката.
Когда выбирать pg_upgrade, а когда лучше dump/restore или репликацию
pg_upgrade хорошо подходит для одиночного сервера PostgreSQL на Linux, типичного VDS, небольшого кластера приложений или базы, где допустимо короткое окно обслуживания. Он особенно полезен, когда объём данных уже слишком велик для комфортного pg_dump и восстановления, но архитектура ещё не требует сложной миграции через логическую репликацию.
Метод dump/restore проще концептуально: выгрузили SQL или custom dump, подняли новый кластер, восстановили, проверили. Он часто удобен для небольших баз, для смены кодировки, очистки исторического мусора или миграции между разными платформами. Минус очевиден: на больших базах это долго, а время простоя может стать неприемлемым.
Логическая репликация помогает сделать почти zero-downtime переход, но требует больше подготовки: публикации, подписки, контроль последовательностей, особенности DDL, отдельная проверка совместимости типов и extensions. Для нагруженных проектов это отличный путь, но для обычного сервера с несколькими базами pg_upgrade часто остаётся самым практичным вариантом.
В этой инструкции разберём именно сценарий pg_upgrade на Linux. Команды будут ориентировочными: пути и имена служб отличаются между Debian/Ubuntu, RHEL-подобными системами, SUSE и сборками из сторонних репозиториев. Перед выполнением сверяйтесь с тем, как PostgreSQL установлен именно у вас.
Подготовка: инвентаризация кластера
Начните не с установки новой версии, а с инвентаризации. Нужно понять текущую версию PostgreSQL, путь к данным, порт, параметры локали, checksums, список баз, размеры, активные extensions и внешние зависимости приложения. Это помогает заранее увидеть несовместимости и оценить длительность окна обслуживания.
sudo -iu postgres psql -c 'SELECT version();'
sudo -iu postgres psql -c 'SHOW data_directory;'
sudo -iu postgres psql -c 'SHOW config_file;'
sudo -iu postgres psql -c 'SHOW hba_file;'
sudo -iu postgres psql -c 'SHOW port;'
sudo -iu postgres psql -c 'SHOW lc_collate;'
sudo -iu postgres psql -c 'SHOW lc_ctype;'
Посмотрите размеры баз и общий объём каталога данных. Это пригодится для выбора режима upgrade: копирование безопаснее для rollback, режим --link быстрее и экономит место, но требует более строгой дисциплины.
sudo -iu postgres psql -c 'SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database ORDER BY pg_database_size(datname) DESC;'
sudo du -sh /var/lib/postgresql
sudo du -sh /var/lib/pgsql
Не все пути будут существовать одновременно. На Debian и Ubuntu обычно используется дерево /var/lib/postgresql/версия/main, а бинарники лежат в /usr/lib/postgresql/версия/bin. В RHEL, AlmaLinux и Rocky Linux часто встречаются /var/lib/pgsql/версия/data и /usr/pgsql-версия/bin. В openSUSE и SLES расположение может зависеть от пакетов и профиля установки.
Отдельно соберите extensions. Это частая причина остановки upgrade: расширение установлено в старом кластере, но его пакет для новой версии забыли поставить.
sudo -iu postgres psql -c 'SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;'
sudo -iu postgres psql -d postgres -c 'SELECT extname, extversion FROM pg_extension ORDER BY 1;'
Команда выше показывает extensions только в выбранной базе. Если у вас несколько баз, проверьте каждую важную базу отдельно. Особенно внимательно относитесь к postgis, pg_stat_statements, timescaledb, pg_partman, uuid-ossp, pg_trgm, btree_gin, pgcrypto и любым расширениям из сторонних репозиториев.
Backup перед major upgrade: не формальность, а точка возврата
Перед major upgrade нужен backup, который вы умеете восстановить. Не «где-то есть ночная копия», а конкретная проверенная точка: когда сделана, где лежит, сколько восстанавливается, есть ли WAL для PITR, хватает ли места, кто принимает решение об откате. Если PostgreSQL обслуживает интернет-магазин, CRM или биллинг, заранее согласуйте RPO и RTO с владельцами сервиса.
Минимальный вариант для небольшой базы — логический дамп. Он не заменяет полноценный физический backup для больших инсталляций, но удобен как дополнительная страховка перед изменениями.
sudo -iu postgres pg_dumpall --globals-only --file=/var/lib/postgresql/globals-before-upgrade.sql
sudo -iu postgres pg_dump --format=custom --file=/var/lib/postgresql/app-before-upgrade.dump appdb
Для крупных баз лучше иметь физический backup: pg_basebackup, pgBackRest, WAL-G, Barman, файловый снимок LVM/ZFS/Btrfs или снимок диска на уровне платформы виртуализации. Если вы как раз строите резервное копирование PostgreSQL, пригодится отдельный разбор про pgBackRest и восстановление PostgreSQL. А для сценариев с точкой восстановления во времени полезно заранее изучить PITR через WAL-архивы.
Важно не название инструмента, а проверяемость. Если вы ни разу не поднимали базу из этой копии на staging, считайте, что backup пока не доказан.
Если используете файловый snapshot, добейтесь консистентности. Для PostgreSQL безопаснее делать его при остановленном кластере либо с корректным backup-процессом и WAL-архивом. Снимок «на лету» без понимания механики может оказаться бесполезным именно тогда, когда понадобится rollback.
Хорошая практика перед upgrade: поднять тестовый сервер, восстановить туда backup и прогнать
pg_upgrade --checkдо реального окна работ.

Установка новой версии PostgreSQL на Linux
На большинстве дистрибутивов можно держать рядом две major-версии PostgreSQL: старую и новую. Это обязательное условие для pg_upgrade: ему нужны старые и новые бинарники, старый каталог данных и заранее инициализированный новый каталог.
Debian и Ubuntu
В Debian/Ubuntu пакеты PostgreSQL обычно поддерживают несколько кластеров через pg_ctlcluster и pg_lsclusters. После установки новой версии система может автоматически создать новый кластер. Его нужно проверить: порт, статус, путь к данным и параметры локали.
sudo apt update
sudo apt install postgresql-16 postgresql-client-16
pg_lsclusters
Если новый кластер создан автоматически и сразу запущен на другом порту, остановите его перед подготовкой. Для upgrade новый кластер должен существовать, но не должен быть запущен в момент выполнения pg_upgrade.
sudo pg_ctlcluster 16 main stop
pg_lsclusters
RHEL, AlmaLinux, Rocky Linux
В RHEL-подобных системах часто используются пакеты с именами вроде postgresql16-server. Инициализация нового кластера обычно выполняется отдельной командой. Названия сервисов зависят от репозитория: это может быть postgresql-16 или иной unit.
sudo dnf install postgresql16-server postgresql16-contrib
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb
sudo systemctl stop postgresql-16
SUSE и другие системы
На SUSE, Gentoo, Arch Linux и в контейнерных окружениях схема та же: установите новую major-версию, создайте новый кластер с теми же ключевыми параметрами и убедитесь, что одновременно доступны старые и новые бинарники. Не полагайтесь на чужие пути из инструкции: найдите свои через which postgres, пакетный менеджер и systemd unit.
command -v postgres
command -v pg_upgrade
sudo systemctl list-units 'postgresql*'
Проверка совместимости: pg_upgrade --check
Перед реальным запуском upgrade обязательно выполните режим проверки. Он не переносит данные, но выявляет часть критичных проблем: несовместимые библиотеки, отсутствующие extensions, неподходящий каталог, запущенные процессы, отличия параметров и другие блокеры.
Пример для Debian/Ubuntu при переходе с PostgreSQL 14 на 16:
sudo pg_ctlcluster 14 main stop
sudo pg_ctlcluster 16 main stop
sudo -iu postgres /usr/lib/postgresql/16/bin/pg_upgrade --check --old-bindir=/usr/lib/postgresql/14/bin --new-bindir=/usr/lib/postgresql/16/bin --old-datadir=/var/lib/postgresql/14/main --new-datadir=/var/lib/postgresql/16/main --old-options='-c config_file=/etc/postgresql/14/main/postgresql.conf' --new-options='-c config_file=/etc/postgresql/16/main/postgresql.conf'
Пример для RHEL-подобной разметки путей:
sudo systemctl stop postgresql-14
sudo systemctl stop postgresql-16
sudo -iu postgres /usr/pgsql-16/bin/pg_upgrade --check --old-bindir=/usr/pgsql-14/bin --new-bindir=/usr/pgsql-16/bin --old-datadir=/var/lib/pgsql/14/data --new-datadir=/var/lib/pgsql/16/data
Если pg_upgrade --check ругается на отсутствующую библиотеку, сначала установите пакет extension для новой версии. Например, если в старом кластере есть PostGIS, пакет для PostgreSQL 16 должен быть установлен до upgrade. Если расширение больше не поддерживается, его нужно удалить, заменить или выбрать другой способ миграции.
Проверьте также параметры shared_preload_libraries. Частая ситуация: в старой версии загружался pg_stat_statements или TimescaleDB, а в новой библиотека не установлена. Новый кластер не стартует, администратор видит общую ошибку systemd, а реальная причина лежит в журнале PostgreSQL.
Выбор режима: copy или --link
По умолчанию pg_upgrade копирует файлы в новый каталог. Это требует больше времени и свободного места, зато старый каталог остаётся независимым. Для аккуратного rollback это самый спокойный вариант: если новая версия не прошла проверку, можно остановить её и вернуться к старому кластеру, пока приложение не успело записать новые данные только в новую базу.
Режим --link создаёт жёсткие ссылки вместо копирования. Он намного быстрее и экономит место, что особенно заметно на VDS с большой базой и ограниченным диском. Но после запуска нового кластера старый уже нельзя безопасно использовать как независимую точку отката: файлы данных связаны. Для --link особенно желателен внешний snapshot или полноценный физический backup перед стартом.
Если у вас есть быстрый снимок диска и понятная процедура восстановления, --link может быть разумным выбором. Если свободного места хватает, а простоя допустимо чуть больше, copy-режим психологически и операционно проще.
Запуск pg_upgrade
В окно обслуживания остановите приложение, воркеры, cron-задачи и всё, что может подключаться к PostgreSQL. Недостаточно остановить только веб-сервер: фоновые очереди, миграции, аналитические задачи и backup-агенты тоже могут держать соединения. Перед остановкой кластера проверьте активность.
sudo -iu postgres psql -c 'SELECT pid, datname, usename, state, application_name FROM pg_stat_activity ORDER BY pid;'
После остановки старого и нового кластера запустите upgrade. Ниже пример без --link, то есть в режиме копирования:
sudo pg_ctlcluster 14 main stop
sudo pg_ctlcluster 16 main stop
sudo -iu postgres /usr/lib/postgresql/16/bin/pg_upgrade --old-bindir=/usr/lib/postgresql/14/bin --new-bindir=/usr/lib/postgresql/16/bin --old-datadir=/var/lib/postgresql/14/main --new-datadir=/var/lib/postgresql/16/main --old-options='-c config_file=/etc/postgresql/14/main/postgresql.conf' --new-options='-c config_file=/etc/postgresql/16/main/postgresql.conf' --jobs=4
А это вариант с --link. Используйте его только если понимаете последствия и имеете внешний rollback-план:
sudo -iu postgres /usr/lib/postgresql/16/bin/pg_upgrade --old-bindir=/usr/lib/postgresql/14/bin --new-bindir=/usr/lib/postgresql/16/bin --old-datadir=/var/lib/postgresql/14/main --new-datadir=/var/lib/postgresql/16/main --old-options='-c config_file=/etc/postgresql/14/main/postgresql.conf' --new-options='-c config_file=/etc/postgresql/16/main/postgresql.conf' --jobs=4 --link
Не запускайте старый кластер «просто посмотреть», если уже начали работу новой версии, особенно при --link. Держите один источник истины. После успешного upgrade внимательно прочитайте вывод: pg_upgrade может создать скрипты для удаления старого кластера и для анализа новой базы. Не выполняйте cleanup до завершения проверок и окончания периода наблюдения.
Старт новой версии и перенос настроек
После успешного pg_upgrade запустите новый кластер. На Debian/Ubuntu проверьте, какой порт назначен новой версии. Нередко старая версия занимала 5432, а новая была создана на 5433. После остановки старой версии нужно привести порт нового кластера к ожидаемому приложением значению.
pg_lsclusters
sudo pg_ctlcluster 16 main start
sudo -iu postgres psql -p 5432 -c 'SELECT version();'
На RHEL-подобных системах старт обычно выглядит через systemd:
sudo systemctl start postgresql-16
sudo systemctl status postgresql-16
sudo -iu postgres psql -c 'SELECT version();'
Не копируйте старый postgresql.conf поверх нового целиком. Между версиями появляются новые параметры, а часть старых может устареть или изменить смысл. Лучше перенести настройки осознанно: listen_addresses, port, max_connections, shared_buffers, effective_cache_size, work_mem, maintenance_work_mem, wal_level, archive_mode, archive_command, shared_preload_libraries, параметры логирования и autovacuum.
То же касается pg_hba.conf. После upgrade легко получить ситуацию, когда база работает локально, но приложение не подключается из-за забытых правил доступа. Сравните старый и новый файл, затем перезагрузите конфигурацию.
sudo -iu postgres psql -c 'SELECT pg_reload_conf();'
Extensions после upgrade
pg_upgrade переносит сведения о расширениях, но это не значит, что сами extensions обновлены до последней версии. После старта нового кластера проверьте доступные обновления расширений в каждой базе. Для встроенных расширений обычно всё просто, а для PostGIS, TimescaleDB и других сложных модулей нужно читать их собственные инструкции совместимости.
sudo -iu postgres psql -d appdb -c 'SELECT name, default_version, installed_version FROM pg_available_extensions WHERE installed_version IS NOT NULL ORDER BY name;'
Если расширение поддерживает обновление SQL-командой, выполняйте его отдельно и контролируемо. Не обновляйте все extensions вслепую на production без теста: иногда новая версия расширения меняет поведение функций, индексов или фоновых задач.
sudo -iu postgres psql -d appdb -c 'ALTER EXTENSION pg_stat_statements UPDATE;'
sudo -iu postgres psql -d appdb -c 'ALTER EXTENSION pg_trgm UPDATE;'
Для extensions, которые используют shared_preload_libraries, после установки пакетов и изменения конфигурации может потребоваться рестарт PostgreSQL, а не простой reload. Если новая версия не стартует, первым делом смотрите журнал службы и лог PostgreSQL, а не меняйте параметры наугад.

vacuumdb, статистика и производительность после upgrade
После major upgrade статистика планировщика может быть неполной или устаревшей. Это значит, что запросы, которые вчера работали быстро, сегодня могут внезапно выбрать неудачный план. Поэтому после запуска новой версии выполните анализ баз. В современных версиях PostgreSQL для этого удобно использовать vacuumdb с режимом анализа после upgrade.
sudo -iu postgres vacuumdb --all --analyze-in-stages
Ключ --analyze-in-stages сначала собирает грубую статистику быстро, чтобы база стала пригодной к работе, а затем уточняет её. Это полезно для больших инсталляций, где полный анализ может занять много времени. Если у вас есть конкретная критичная база, можно начать с неё:
sudo -iu postgres vacuumdb --dbname=appdb --analyze-in-stages
Не путайте VACUUM FULL и обычный анализ. После upgrade обычно не нужен массовый VACUUM FULL: он блокирует таблицы и переписывает данные. Если задача — обновить статистику, используйте ANALYZE или vacuumdb --analyze-in-stages. Борьба с bloat — отдельная операция, которую лучше планировать после стабилизации новой версии.
Проверки приложения и базы
После старта новой версии не ограничивайтесь командой SELECT version();. Проверьте реальные сценарии: логин пользователя, создание заказа, запись в очередь, фоновые задачи, отчёты, миграции, чтение и запись файлов, если приложение хранит ссылки на объекты. Если есть read-only replica, PgBouncer, cron-скрипты или BI-подключения, они тоже должны пройти проверку.
Полезный минимум для PostgreSQL:
- проверить, что приложение подключается к ожидаемому порту и базе;
- посмотреть ошибки в логах PostgreSQL и приложения;
- проверить количество соединений и долгие транзакции;
- сравнить список баз, ролей и extensions;
- запустить несколько критичных SQL-запросов и сравнить время выполнения;
- убедиться, что backup после upgrade снова работает.
sudo -iu postgres psql -c 'SELECT datname, numbackends, xact_commit, xact_rollback FROM pg_stat_database ORDER BY datname;'
sudo -iu postgres psql -c 'SELECT pid, datname, state, wait_event_type, wait_event FROM pg_stat_activity ORDER BY pid;'
Если используется PgBouncer, проверьте его конфигурацию отдельно. Иногда после upgrade меняется порт PostgreSQL, socket directory или TLS-настройки, а PgBouncer продолжает смотреть на старый endpoint. В результате база уже обновлена, но приложение получает ошибки подключения.
Rollback: как возвращаться назад без хаоса
Rollback нужно описать до начала работ. В идеале у вас есть короткий документ: критерии отката, ответственный, команды остановки новой версии, способ восстановления старой, ожидаемое время и проверка после возврата. Самый опасный rollback — тот, который придумывают ночью под давлением.
В copy-режиме базовый сценарий проще. Если приложение ещё не переключено или тесты сразу выявили проблему, остановите новый кластер, убедитесь, что старый каталог не изменялся, верните порт и запустите старую версию. Но если приложение уже писало данные в новую версию, простой возврат к старой приведёт к потере этих записей. Здесь нужен бизнес-выбор: принять потерю в пределах RPO, переносить изменения вручную или продолжать исправлять новую версию.
sudo pg_ctlcluster 16 main stop
sudo pg_ctlcluster 14 main start
sudo -iu postgres psql -c 'SELECT version();'
При --link не рассчитывайте на старый каталог как на безопасный rollback после запуска новой версии. Используйте внешний snapshot, физический backup или восстановление на отдельный сервер. Поэтому перед --link особенно важно остановиться и честно ответить: вы точно знаете, как вернуть диск или базу в предыдущее состояние?
Хорошая практика для VDS — перед окном работ иметь не только PostgreSQL backup, но и снимок всего сервера, если такая возможность доступна в вашей инфраструктуре. Он не отменяет нормальные резервные копии базы, но ускоряет возврат конфигурации, пакетов и systemd unit к согласованному состоянию.
Типовые ошибки при PostgreSQL major upgrade
Первая частая ошибка — забытые extensions. Старый кластер годами жил с расширением, установленным вручную, а в новой версии пакет не поставили. pg_upgrade --check обычно ловит это заранее, но только если запускать его внимательно и не откладывать проверку на последние минуты окна.
Вторая ошибка — конфликт портов. Новый кластер уже запущен на 5433, старый остановили, приложение ждёт 5432, администратор меняет конфиги в спешке. Перед upgrade зафиксируйте, какой порт должен быть итоговым и где он задаётся: в postgresql.conf, unit-файле, переменных окружения, PgBouncer или настройках приложения.
Третья ошибка — слепое копирование старого конфига. PostgreSQL развивается, и старые параметры могут быть удалены. Если новая версия не стартует после копирования, вернитесь к чистому конфигу новой версии и переносите настройки по одной группе.
Четвёртая ошибка — отсутствие анализа после upgrade. База стартует, приложение открывается, но через час появляются медленные запросы и рост CPU. Часто причина банальна: статистику не обновили через vacuumdb. Запланируйте анализ как обязательный этап, а не как необязательную оптимизацию.
Пятая ошибка — преждевременное удаление старого кластера. Не запускайте cleanup-скрипты сразу после первого успешного подключения. Подождите период наблюдения, убедитесь в backup новой версии, проверьте мониторинг и только потом удаляйте старые данные.
Короткий runbook для рабочего окна
Ниже компактная последовательность, которую удобно адаптировать под свой сервер. Не копируйте её бездумно: версии, пути, сервисы и порты замените на свои.
- За несколько дней до работ: восстановить backup на тестовом сервере и выполнить
pg_upgrade --check. - Поставить пакеты новой версии PostgreSQL и extensions для новой версии.
- Собрать инвентаризацию: версии, пути, размеры, extensions, параметры, роли, список приложений.
- Проверить свободное место, особенно если выбран copy-режим.
- Перед окном: сделать проверенный backup и при необходимости snapshot.
- Остановить приложение, воркеры, cron и внешние подключения.
- Остановить старый и новый кластеры PostgreSQL.
- Запустить
pg_upgrade --checkещё раз на production. - Запустить
pg_upgradeв выбранном режиме. - Перенести настройки, проверить порт,
pg_hba.confи preload libraries. - Запустить новую версию PostgreSQL и проверить подключение.
- Выполнить
vacuumdb --analyze-in-stages. - Проверить приложение, логи, мониторинг, backup и критичные запросы.
- Оставить старый кластер до завершения периода наблюдения, если это возможно.
Итоги
pg_upgrade делает PostgreSQL major upgrade на Linux быстрым, но не магическим. Он не заменяет backup, не решает несовместимость extensions, не переносит за вас здравый смысл из старого конфига в новый и не гарантирует rollback после записи данных в новую версию. Зато при правильной подготовке это надёжный и предсказуемый инструмент для администраторов, которые хотят обновлять PostgreSQL без многочасового dump/restore.
Мой практический совет простой: сначала прогоните весь сценарий на копии, затем напишите короткий runbook, затем делайте production. Если upgrade занимает десять минут, а подготовка два дня — это нормально. В базах данных спокойная подготовка почти всегда дешевле героического восстановления.


