PostgreSQL не поддерживает in-place upgrade между мажорными версиями (15 → 17, 17 → 18). Данные нужно переносить логически: через дамп и восстановление.
Почему это важно:
- ✅ Новые версии = исправления безопасности, оптимизации, новые функции
- ✅ Поддержка актуальных клиентов (Nextcloud, Mastodon, Authelia требуют свежие версии)
- ✅ Предсказуемость: один и тот же процесс для любого проекта
Сложность в Docker:
- ❌ Нельзя просто поменять тег образа - формат данных несовместим
- ❌
pg_upgradeтребует одновременного доступа к старым и новым бинарникам - ломает изоляцию контейнеров - ✅ Решение:
pg_dumpall→ новый контейнер →psql < dump
📋 Предварительные требования
Перед началом убедись, что:
- Есть доступ к терминалу с правами на
dockerиdocker compose - Свободно ~2× размера базы на диске (для дампа + архива)
- Знаешь имя контейнера БД (
nextcloud-postgres) и пользователя (nextcloud) - Есть файл
docker-compose.ymlс описанием сервисаpostgres - Приложение (Nextcloud/Mastodon) остановлено или переведено в режим обслуживания
💡 Если не уверен - сделай полный бэкап тома перед началом:
tar czf backup-volume.tar.gz /var/lib/docker/volumes/...
Шаг 1: Логический бэкап
# Создаём дамп всех баз, ролей и глобальных настроек
docker exec nextcloud-postgres pg_dumpall -U nextcloud > pg_dumpall_$(date +%F).sql
# Проверяем размер и целостность
ls -lh pg_dumpall_*.sql
tail -10 pg_dumpall_*.sql # должен заканчиваться ";"
⚠️ Если пользователь не имеет прав суперпользователя, используй
-U postgres.
Шаг 2: Физический архив тома (страховка)
# Останавливаем БД
docker compose stop postgres
# Находим имя тома
docker volume ls | grep nextcloud_database
# Архивируем сырые данные
sudo tar czf postgres-volume-$(date +%F).tar.gz \
-C /var/lib/docker/volumes/nextcloud_database/_data .
Шаг 3: Удаление старого контейнера и тома
docker compose down postgres
docker volume rm nextcloud_database
Шаг 4: Обновление docker-compose.yml
Выбери один из вариантов в зависимости от целевой версии.
✅ Вариант А: PostgreSQL 15 / 16 / 17 (без изменений путей)
services:
postgres:
image: postgres:17-alpine # или 15, 16
volumes:
- database:/var/lib/postgresql/data
✅ Вариант Б: PostgreSQL 18+ (новый стандарт)
services:
postgres:
image: postgres:18-alpine
volumes:
- database:/var/lib/postgresql # ← монтируем родительскую директорию
# Контейнер сам создаст /var/lib/postgresql/18/docker внутри тома
🔧 Вариант В: PostgreSQL 18+ с обратной совместимостью
services:
postgres:
image: postgres:18-alpine
volumes:
- database:/var/lib/postgresql/data
environment:
PGDATA: /var/lib/postgresql/data # ← принудительно старый путь
💡 Рекомендация: используй Вариант Б. Он соответствует стандарту Debian/Alpine и упрощает будущие
pg_upgrade --link.
Шаг 5: Запуск новой БД
docker compose up -d postgres
sleep 20 # ждём initdb
docker exec nextcloud-postgres pg_isready -U nextcloud -d nextcloud
# Ожидаемо: "accepting connections"
Шаг 6: Восстановление данных
docker exec -i nextcloud-postgres psql -U nextcloud < pg_dumpall_*.sql
🟡 Нормальные ошибки в логах:
ERROR: role "nextcloud" already exists ERROR: database "nextcloud" already existsНовый контейнер уже создал роль/БД из
stack.env.psqlигнорирует дубли и корректно заливает таблицы. Просто продолжай.
Шаг 7: Финальная проверка
# Версия
docker exec nextcloud-postgres psql -U nextcloud -c "SELECT version();"
# Путь данных
docker exec nextcloud-postgres psql -U nextcloud -c "SHOW data_directory;"
# ≤17: /var/lib/postgresql/data
# ≥18: /var/lib/postgresql/18/docker
# Таблицы
docker exec nextcloud-postgres psql -U nextcloud -d nextcloud -c "\dt"
🔄 Откат (если что-то пошло не так)
docker compose down
# Возвращаем старый образ и путь (пример для 17)
sed -i 's/postgres:18/postgres:17/' docker-compose.yml
sed -i 's|/var/lib/postgresql$|/var/lib/postgresql/data|' docker-compose.yml
# Восстанавливаем том
sudo rm -rf /var/lib/docker/volumes/nextcloud_database/_data/*
sudo tar xzf postgres-volume-*.tar.gz -C /var/lib/docker/volumes/nextcloud_database/_data/
docker compose up -d postgres
⚠️ FAQ
# "Что будет, если запустить 18 с volumes:/var/lib/postgresql/data?"
→ Контейнер не найдёт данные по новому пути, выполнит initdb и создаст пустую БД.
Старые файлы останутся в томе нетронутыми.
# "Как проверить путь после запуска?"
→ SHOW data_directory; внутри psql.
# "А можно использовать pg_upgrade вместо дампа?"
→ В Docker это требует одновременного маунта старых/новых бинарников, что ломает изоляцию.
Dump/restore надёжнее для контейнерных сред.
# "Обновится ли 18→19 так же?"
→ Да. При каждом мажорном апгрейде меняется поддиректория (18/docker → 19/docker).
Один том `/var/lib/postgresql` будет содержать все версии параллельно.