Бэкап и восстановление Payload CMS
Payload CMS хранит схему контента в коде, а не в базе — это удобно для разработки, но путает подход к бэкапам. Многие копируют только базу данных и удивляются, почему после восстановления пропадают загруженные файлы или ломается интеграция с S3. В этой статье разберём, что именно нужно бэкапить в Payload, как настроить автоматическое резервное копирование на сервере и как восстановиться без потери данных и без танцев с миграциями.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что на самом деле нужно бэкапить
Payload — TypeScript-first headless CMS, и это определяет структуру бэкапа. У проекта есть три независимых источника состояния, и терять любой из них — проблема:
- База данных — MongoDB или PostgreSQL (Payload 3.x поддерживает оба через официальные адаптеры). Здесь лежат документы коллекций, глобалы, связи, черновики и версии, если включён
versions.drafts. - Медиафайлы — если используете
staticDir(локальное хранилище) или media собирается черезuploadколлекции без облачного адаптера, файлы физически лежат на диске сервера вmedia/или другой указанной папке. - Код и конфиг —
payload.config.ts, схемы коллекций, кастомные хуки, access control. Это то самое «код как источник схемы», о котором говорят в документации Payload: без этого файла база данных превращается в набор документов без структуры.
Отдельно стоит переменная окружения PAYLOAD_SECRET — она используется для подписи JWT-токенов аутентификации. Потеряете её при восстановлении на новом сервере — все активные сессии и API-ключи, завязанные на неё, станут невалидны, пользователям придётся логиниться заново. Это не катастрофа, но лучше знать заранее и хранить секрет отдельно от репозитория (в password-менеджере или зашифрованном файле).
Если используете облачное хранилище (@payloadcms/storage-s3, @payloadcms/storage-vercel-blob и т.п.), файлы уже не на вашем сервере — бэкапить их отдельно не нужно, но стоит убедиться, что у бакета настроено версионирование или репликация на стороне провайдера.
Бэкап MongoDB для Payload
Если проект работает на MongoDB (адаптер @payloadcms/db-mongodb), используйте штатные утилиты mongodump/mongorestore — они снимают консистентный дамп со всеми коллекциями и индексами.
# Полный дамп базы Payload
mongodump --uri="mongodb://user:password@localhost:27017/payload_db" \
--out=/backup/payload/$(date +%Y%m%d_%H%M%S)
# Сжатый дамп в один архив (удобнее хранить и передавать)
mongodump --uri="mongodb://user:password@localhost:27017/payload_db" \
--archive=/backup/payload/dump_$(date +%Y%m%d).gz --gzip
Восстановление зеркально:
mongorestore --uri="mongodb://user:password@localhost:27017/payload_db" \
--archive=/backup/payload/dump_20260901.gz --gzip --drop
Флаг --drop пересоздаёт коллекции перед восстановлением — без него старые документы, которых нет в дампе, останутся в базе и получится смесь двух состояний. Если база большая (десятки гигабайт медиаметаданных и версий), добавьте --numParallelCollections=4 — дамп и восстановление пойдут параллельно по коллекциям.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверБэкап PostgreSQL для Payload
С адаптером @payloadcms/db-postgres (на Drizzle ORM) бэкап делается стандартными средствами PostgreSQL — pg_dump в формате custom, чтобы восстановление можно было делать выборочно и с сжатием:
# Дамп в custom-формате
pg_dump -U payload_user -h localhost -d payload_db \
-F c -f /backup/payload/payload_$(date +%Y%m%d).dump
# Восстановление в чистую базу
createdb -U payload_user payload_db_restored
pg_restore -U payload_user -d payload_db_restored \
--clean --if-exists /backup/payload/payload_20260901.dump
Payload с Postgres-адаптером генерирует схему через миграции (payload migrate), поэтому при восстановлении на новом сервере важно, чтобы версия кода совпадала с версией схемы в дампе — иначе получите рассинхрон между payload.config.ts и реальными таблицами. Подробнее про сам процесс установки и настройки Postgres под такие нагрузки — в статье про настройку PostgreSQL на VPS.
Бэкап медиафайлов и загрузок
Если медиа хранится локально, бэкап — это обычная синхронизация директории. Простой вариант через rsync с ротацией по датам:
#!/bin/bash
MEDIA_DIR="/var/www/payload-app/media"
BACKUP_DIR="/backup/payload/media/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
rsync -a --delete "$MEDIA_DIR/" "$BACKUP_DIR/"
Для больших медиатек (тысячи изображений с несколькими размерами, если у вас настроены imageSizes в конфиге upload-коллекции) полный rsync каждый раз избыточен — лучше делать полный снимок раз в неделю и инкрементальные через rsync --link-dest на промежуточные дни, чтобы не плодить копии неизменных файлов.
Если у вас уже настроено S3-совместимое хранилище для медиа (MinIO на своём сервере или внешний провайдер), сам процесс бэкапа файлов переносится на уровень хранилища — почитайте про развёртывание MinIO в Docker Compose, там же описано, как настраивать репликацию бакетов.
Автоматизация: единый скрипт и cron
Собрать всё в один скрипт, который бэкапит базу, медиа и конфиг, и упаковывает в один архив с ротацией:
#!/bin/bash
set -e
APP_DIR="/var/www/payload-app"
BACKUP_ROOT="/backup/payload"
DATE=$(date +%Y%m%d_%H%M%S)
WORK_DIR="$BACKUP_ROOT/tmp_$DATE"
RETENTION_DAYS=14
mkdir -p "$WORK_DIR"
# 1. База данных (пример для MongoDB, для Postgres замените на pg_dump)
mongodump --uri="$DATABASE_URI" --archive="$WORK_DIR/db.gz" --gzip
# 2. Медиафайлы
rsync -a "$APP_DIR/media/" "$WORK_DIR/media/"
# 3. Конфиг и .env (без node_modules и .git)
rsync -a --exclude 'node_modules' --exclude '.git' --exclude 'media' \
"$APP_DIR/" "$WORK_DIR/app/"
# 4. Упаковка в один архив
tar -czf "$BACKUP_ROOT/payload_backup_$DATE.tar.gz" -C "$BACKUP_ROOT" "tmp_$DATE"
rm -rf "$WORK_DIR"
# 5. Ротация старых бэкапов
find "$BACKUP_ROOT" -name "payload_backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: payload_backup_$DATE.tar.gz"
Добавьте в cron ежедневный запуск ночью, когда нагрузка на сервер минимальна:
# crontab -e
0 3 * * * /usr/local/bin/payload-backup.sh >> /var/log/payload-backup.log 2>&1
Скрипт хранит бэкапы локально — этого достаточно как первая линия защиты, но при отказе диска или всего сервера локальные копии не спасут. Обязательно выгружайте архивы на отдельное хранилище: другой сервер, S3-бакет через rclone, либо специализированный инструмент вроде restic с шифрованием и дедупликацией — про его настройку есть отдельный разбор бэкапа и восстановления через restic.
Проверка бэкапов и disaster recovery
Бэкап, который ни разу не восстанавливали, — это не бэкап, а надежда. Раз в месяц имеет смысл прогонять полное восстановление на тестовом сервере или отдельном контейнере:
# Разворачиваем свежий тестовый инстанс
docker run -d --name payload-test-mongo -p 27018:27017 mongo:7
# Восстанавливаем дамп
mongorestore --uri="mongodb://localhost:27018/payload_db" \
--archive=/backup/payload/dump_20260901.gz --gzip
# Разворачиваем код на нужном коммите и проверяем, что Payload стартует
cd /tmp/payload-test && npm ci && npm run dev
Если после старта админка Payload открывается на /admin и коллекции видны с данными — восстановление рабочее. Проверяйте отдельно, что медиафайлы физически на месте и ссылки на них в базе (url в документах upload-коллекций) не битые — это частая причина, когда бэкап базы и бэкап файлов делались не синхронно и разъехались по времени.
Полный disaster recovery план для проекта на Payload должен покрывать не только базу — весь стек вокруг него, включая сам VPS. Если сценарий предполагает переезд на новый сервер целиком, полезно заранее продумать миграцию базы данных между серверами — там описаны нюансы переноса без даунтайма, которые пригодятся и для Payload-проектов на Postgres.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужно ли бэкапить node_modules и собранный build?
Нет — это восстанавливаемые артефакты, npm ci и npm run build пересоздадут их за минуты. Бэкапьте исходный код (или полагайтесь на git-репозиторий), базу и медиа.
Что делать, если PAYLOAD_SECRET утерян?
Сгенерируйте новую строку и пропишите в .env, но учтите: все существующие JWT-сессии станут невалидны, пользователям придётся войти заново. Данные в базе при этом не теряются.
Как часто делать бэкапы для активного контент-проекта?
Для сайта с ежедневными правками контента — минимум раз в сутки для базы, для медиа можно реже (раз в 2-3 дня), если новые файлы загружаются нечасто. Для интернет-магазина или проекта с частыми транзакциями рассмотрите инкрементальные бэкапы базы каждые несколько часов.
Можно ли бэкапить Payload через встроенные средства самой CMS?
У Payload нет собственного модуля экспорта/импорта всего проекта «из коробки» — есть API для экспорта отдельных коллекций через REST или GraphQL, но для полного бэкапа инфраструктуры используются штатные инструменты СУБД и файловой системы, описанные выше.
Что если используется Payload с двумя базами данных одновременно (например, миграция с Mongo на Postgres)?
Бэкапьте обе до завершения переезда и держите план отката на исходную базу, пока не убедитесь, что новая схема стабильно работает хотя бы неделю под реальной нагрузкой.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →