Skip to content

Зачем это нужно

Начиная с версии 7.1.0 боту требуется PostgreSQL 18 или новее — используется встроенная функция uuidv7(). На PostgreSQL 17 приложение не запустится.

Почему нельзя просто сменить тег образа ​

Смена postgres:17-alpine на postgres:18-alpine не переносит данные. Причин две:

  1. Формат данных несовместим между мажорными версиями. PostgreSQL 18 не откроет каталог, созданный семнадцатой версией, и контейнер упадёт с ошибкой database files are incompatible with server.
  2. В образе 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:

bash
docker compose exec -T db pg_dump -U postgres -d postgres > pg17_dump.sql

Проверьте, что дамп не пустой и завершился корректно:

bash
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: Остановите бота ​

bash
docker compose down

Без -v. Том со старыми данными должен остаться на месте.

Шаг 3: Обновите docker-compose.yaml ​

В сервисе db поменяйте три вещи: версию образа, путь монтирования и имя тома.

yaml
  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 внизу файла:

yaml
volumes:
  rwp_shop_db_data:
  rwp_shop_db_data_18:

Почему новый том

Новое имя (rwp_shop_db_data_18) гарантирует, что PostgreSQL 18 стартует на пустом каталоге и не встретит несовместимые файлы. Старый том rwp_shop_db_data остаётся нетронутым — это ваша точка отката. Удалить его можно через несколько дней, когда убедитесь, что всё работает.

Шаг 4: Поднимите только базу ​

bash
docker compose up -d db

Дождитесь готовности — команда должна ответить accepting connections:

bash
docker compose exec db pg_isready -U postgres

Убедитесь, что версия действительно новая:

bash
docker compose exec db psql -U postgres -d postgres -c "SHOW server_version;"

Шаг 5: Залейте дамп ​

bash
docker compose exec -T db psql -U postgres -d postgres < pg17_dump.sql

Отдельные NOTICE в выводе — норма. Строки с ERROR — нет: остановитесь и разберитесь, прежде чем запускать бота.

Проверьте, что данные на месте (число таблиц должно быть больше нуля):

bash
docker compose exec db psql -U postgres -d postgres -c "\dt" | tail -n 5

Шаг 6: Запустите бота ​

bash
docker compose up -d

Проверьте, что миграции применились и ошибок подключения нет:

bash
docker compose logs -f bot

Дополнительно проверьте эндпоинт здоровья — в ответе поле postgres должно быть "status": "pass".

После этого зайдите в админ-панель и убедитесь, что видны клиенты, платежи и подписки.

Откат ​

Если что-то пошло не так, вернуться к прежнему состоянию можно за минуту — старый том не изменялся:

bash
docker compose down

Верните в docker-compose.yaml прежние значения:

yaml
    image: postgres:17-alpine
    volumes:
      - rwp_shop_db_data:/var/lib/postgresql/data

И поднимите обратно вместе со старой версией образа бота:

bash
docker compose up -d

Важно

Откат возвращает данные на момент шага 1. Всё, что клиенты сделали после запуска на PostgreSQL 18, будет потеряно. Поэтому проверку из шага 6 стоит проводить сразу, а не через сутки.

Очистка ​

Когда убедитесь, что всё стабильно (разумно подождать несколько дней), удалите старый том и дамп:

bash
docker volume rm rwp_shop_rwp_shop_db_data
rm pg17_dump.sql

Имя тома в Docker формируется как <имя проекта>_<имя тома> — сверьтесь со списком:

bash
docker volume ls | grep rwp_shop

После удаления старого тома уберите rwp_shop_db_data из секции volumes в compose-файле.

Если что-то не получилось ​

СимптомПричина и решение
database files are incompatible with serverPostgreSQL 18 подняли на старом томе. Убедитесь, что в volumes указан новый том и путь /var/lib/postgresql
База пустая после запуска, бот создал схему с нуляДамп не залили или залили до готовности базы. Остановите бота, удалите новый том, повторите с шага 4
role "postgres" does not existВ .env другое значение POSTGRES_USER. Подставьте его вместо postgres во все команды на этой странице
Дамп оборвался на шаге 1Чаще всего кончилось место. Проверьте df -h и снимите дамп заново

Остальные ситуации — на странице Устранение неполадок.