Зачем это нужно
Начиная с версии 7.1.0 боту требуется PostgreSQL 18 или новее — используется встроенная функция uuidv7(). На PostgreSQL 17 приложение не запустится.
Почему нельзя просто сменить тег образа
Смена postgres:17-alpine на postgres:18-alpine не переносит данные. Причин две:
- Формат данных несовместим между мажорными версиями. PostgreSQL 18 не откроет каталог, созданный семнадцатой версией, и контейнер упадёт с ошибкой
database files are incompatible with server. - В образе 18 изменился путь к данным. Раньше том монтировали в
/var/lib/postgresql/data, теперь официальный образ хранит кластер в/var/lib/postgresql/18/dockerи объявляет томом весь каталог/var/lib/postgresql. Поэтому в compose путь монтирования тоже меняется.
Самый быстрый и предсказуемый способ — выгрузить базу через pg_dump на старой версии и залить её в чистый том новой. Для базы обычного магазина это занимает минуты.
Перед началом
- Запланируйте короткое сервисное окно: бот будет остановлен на время переноса.
- Проверьте, что на диске есть свободное место под дамп и второй том БД:
df -h. - Перейдите в директорию бота (
cd /opt/rwp_shopили ваш фактический путь).
Внимание
Ни на одном шаге не выполняйте docker compose down -v и не удаляйте старый том. Пока старый том цел, откат занимает одну минуту.
Шаг 1: Снимите дамп на работающей PostgreSQL 17
Бот ещё работает — дамп снимается со старого контейнера его же версией pg_dump:
docker compose exec -T db pg_dump -U postgres -d postgres > pg17_dump.sqlПроверьте, что дамп не пустой и завершился корректно:
ls -lh pg17_dump.sql
grep -c "PostgreSQL database dump complete" pg17_dump.sqlКоманда должна вывести 1. Если 0 — дамп оборвался (чаще всего кончилось место), не продолжайте.
Строка \unrestrict в конце файла
Начиная с версий 18, 17.6 и 16.10 pg_dump оборачивает дамп в мета-команды \restrict / \unrestrict, поэтому последней строкой файла будет \unrestrict <случайный токен>, а маркер завершения — предпоследним блоком. Это нормально: так psql не выполняет мета-команды из подменённого дампа. Именно поэтому проверять содержимое надо через grep, а не tail -n 1.
Побочный эффект: такой дамп не зальётся старым psql (до 17.6), который не знает этих команд. При переходе на 18 это не мешает — восстанавливаете вы более новым клиентом.
Совет
Скопируйте pg17_dump.sql на другую машину. Это ваш единственный носитель данных на время миграции.
Шаг 2: Остановите бота
docker compose downБез -v. Том со старыми данными должен остаться на месте.
Шаг 3: Обновите docker-compose.yaml
В сервисе db поменяйте три вещи: версию образа, путь монтирования и имя тома.
db:
image: postgres:18-alpine
container_name: rwp_shop_db
restart: unless-stopped
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
- TZ=UTC
ports:
- "127.0.0.1:9999:5432"
volumes:
- rwp_shop_db_data_18:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 3s
timeout: 10s
retries: 3И в секции volumes внизу файла:
volumes:
rwp_shop_db_data:
rwp_shop_db_data_18:Почему новый том
Новое имя (rwp_shop_db_data_18) гарантирует, что PostgreSQL 18 стартует на пустом каталоге и не встретит несовместимые файлы. Старый том rwp_shop_db_data остаётся нетронутым — это ваша точка отката. Удалить его можно через несколько дней, когда убедитесь, что всё работает.
Шаг 4: Поднимите только базу
docker compose up -d dbДождитесь готовности — команда должна ответить accepting connections:
docker compose exec db pg_isready -U postgresУбедитесь, что версия действительно новая:
docker compose exec db psql -U postgres -d postgres -c "SHOW server_version;"Шаг 5: Залейте дамп
docker compose exec -T db psql -U postgres -d postgres < pg17_dump.sqlОтдельные NOTICE в выводе — норма. Строки с ERROR — нет: остановитесь и разберитесь, прежде чем запускать бота.
Проверьте, что данные на месте (число таблиц должно быть больше нуля):
docker compose exec db psql -U postgres -d postgres -c "\dt" | tail -n 5Шаг 6: Запустите бота
docker compose up -dПроверьте, что миграции применились и ошибок подключения нет:
docker compose logs -f botДополнительно проверьте эндпоинт здоровья — в ответе поле postgres должно быть "status": "pass".
После этого зайдите в админ-панель и убедитесь, что видны клиенты, платежи и подписки.
Откат
Если что-то пошло не так, вернуться к прежнему состоянию можно за минуту — старый том не изменялся:
docker compose downВерните в docker-compose.yaml прежние значения:
image: postgres:17-alpine
volumes:
- rwp_shop_db_data:/var/lib/postgresql/dataИ поднимите обратно вместе со старой версией образа бота:
docker compose up -dВажно
Откат возвращает данные на момент шага 1. Всё, что клиенты сделали после запуска на PostgreSQL 18, будет потеряно. Поэтому проверку из шага 6 стоит проводить сразу, а не через сутки.
Очистка
Когда убедитесь, что всё стабильно (разумно подождать несколько дней), удалите старый том и дамп:
docker volume rm rwp_shop_rwp_shop_db_data
rm pg17_dump.sqlИмя тома в Docker формируется как <имя проекта>_<имя тома> — сверьтесь со списком:
docker volume ls | grep rwp_shopПосле удаления старого тома уберите rwp_shop_db_data из секции volumes в compose-файле.
Если что-то не получилось
| Симптом | Причина и решение |
|---|---|
database files are incompatible with server | PostgreSQL 18 подняли на старом томе. Убедитесь, что в volumes указан новый том и путь /var/lib/postgresql |
| База пустая после запуска, бот создал схему с нуля | Дамп не залили или залили до готовности базы. Остановите бота, удалите новый том, повторите с шага 4 |
role "postgres" does not exist | В .env другое значение POSTGRES_USER. Подставьте его вместо postgres во все команды на этой странице |
| Дамп оборвался на шаге 1 | Чаще всего кончилось место. Проверьте df -h и снимите дамп заново |
Остальные ситуации — на странице Устранение неполадок.