Бэкапы ERPNext: bench backup, восстановление и проверка, что копия живая
Бэкап ERPNext делает команда bench backup, но без флага --with-files она сохраняет только базу и site_config: вложения останутся без копии. Ниже: что копировать, как поставить расписание, где хранить, как восстановить через bench restore и как проверить, что копия живая.
Что нужно копировать в ERPNext: база, файлы и настройки сайта
Данные ERPNext живут в трёх местах, и копировать нужно все три. Первое — база данных сайта: документы, справочники, проводки, настройки. Второе — файлы: вложения, которые пользователи прикрепляют к документам, лежат не в базе, а на диске в каталогах public/files и private/files внутри папки сайта. Третье — конфигурация сайта site_config.json, где хранятся параметры подключения и ключ шифрования. Если вы разворачиваете систему по схеме ERPNext под ключ, я включаю проверку всех трёх составляющих в приёмку, а условия сопровождения смотрите на странице продукта.
Команда bench backup по умолчанию снимает только базу. Это видно в исходниках Frappe версии 15: функция backup в модуле commands/site.py передаёт в генератор копий параметр ignore_files=not with_files, то есть пока вы не указали --with-files, файлы игнорируются. На выходе получается набор файлов в каталоге sites/<сайт>/private/backups: архив базы ...-database.sql.gz и копия конфигурации ...-site_config_backup.json. С флагом --with-files добавляются два архива, ...-files.tar для публичных и ...-private-files.tar для приватных вложений, а с флагом --compress вместо tar получается tgz.
Приватные файлы — самая опасная часть. Счета, акты, сканы договоров и фотографии с объектов обычно прикрепляют к документам, и в базе остаётся лишь ссылка на файл. Если восстановить только дамп, документ откроется, а при попытке скачать вложение вы получите ошибку. Поэтому в моих проектах «копия» всегда означает комплект из четырёх файлов с одинаковой временной меткой в имени, а не один архив базы. Ниже таблица, которая помогает быстро понять, что вы получите при разном запуске.
| Команда | База | Публичные файлы | Приватные файлы | site_config |
|---|---|---|---|---|
| bench --site сайт backup | да | нет | нет | да |
| bench --site сайт backup --with-files | да | да | да | да |
| bench --site сайт backup --with-files --compress | да | да (tgz) | да (tgz) | да |
Таблица составлена по параметрам команды в версии 15. В своей версии сверьте вывод bench backup --help: набор флагов с годами менялся.
- Архив базы данных: файл `...-database.sql.gz` (без него восстановление невозможно вообще).
- Публичные и приватные вложения: два tar-архива, появляются только с `--with-files`.
- Копия `site_config.json`: в ней лежит ключ шифрования, без него не расшифровать сохранённые пароли и ключи интеграций.
- Одинаковая метка времени в именах всех четырёх файлов: так видно, что это один комплект.
Как сделать бэкап командой bench backup и поставить его на расписание
Разовая копия делается из каталога bench под тем пользователем Linux, которому принадлежит установка. Если запустить команду от root, файлы получат неправильного владельца, и потом обычный пользователь не сможет их прочитать или удалить. Рабочий вариант для установки без контейнеров такой:
cd ~/frappe-bench
bench --site erp.example.com backup --with-files --compress
ls -lh sites/erp.example.com/private/backups/После команды в каталоге должны лежать четыре свежих файла. Размер архива базы сравните со вчерашним: если он резко уменьшился, что-то не так с самой копией. Флаг --backup-path позволяет сразу писать комплект в другой каталог, например на примонтированный диск, а --exclude исключает тяжёлые журнальные DocType из дампа.
Расписание в обычной установке ставит команда bench setup backups. Я прочитал её код в репозитории bench: она добавляет в crontab пользователя задачу «каждые 6 часов» с командой bench --verbose --site all backup. Обратите внимание: там нет --with-files. Значит, штатное расписание копирует только базу, а вложения остаются без защиты. Свою строку лучше написать самостоятельно:
# crontab -e под пользователем bench
0 2 * * * cd /home/frappe/frappe-bench && /usr/local/bin/bench --site all backup --with-files >> logs/backup.log 2>&1Время 02:00 условное, выберите окно, когда никто не работает: на крупных базах дамп заметно грузит процессор, а tar по вложениям нагружает диск.
Для Docker-установки принцип тот же, но команда выполняется внутри контейнера. В документации frappe_docker для этого приведён пример строки cron: docker compose -p erpnext exec backend bench --site all backup --with-files. А вот скрипт easy-install, которым многие ставят ERPNext и Frappe CRM за пять минут, подключает контейнер с планировщиком ofelia и расписанием @every 6h, и команда у него bench --site all backup без файлов. Проверьте, что получилось у вас: откройте файл .env проекта и посмотрите на переменную BACKUP_CRONSTRING, затем зайдите в private/backups и убедитесь, что рядом с базой лежат tar-архивы вложений. Если их нет, расписание нужно дополнить.
Отдельная мелочь: bench --site all backup снимает все сайты в bench, а --site имя только один. Для компании с одним сайтом разницы нет, но при нескольких тестовых сайтах рядом вы будете копировать лишнее и заполните диск. Я оставляю --site all только на выделенном сервере, где живёт один рабочий сайт.
Где хранить копии: retention, шифрование и резервный сервер
Копия, которая лежит рядом с базой на том же диске, не защищает от потери диска, ошибки администратора и шифровальщика. Правило простое: минимум одна копия вне сервера. Общий принцип хорошо описан в статье про правило бэкапа 3-2-1, а здесь я разберу то, что специфично для Frappe. Каталог sites/<сайт>/private/backups — это только место, куда команда складывает комплект. Выносить его на второй сервер нужно отдельной задачей: rsync по ключу, restic в объектное хранилище или копирование на NAS.
Теперь про глубину хранения. В главе нашей базы знаний было написано, что bench backup удаляет архивы старше суток. В исходниках версии 15 я такого правила для самой команды не нашёл. Нашёл другое: в System Settings есть поле «Number of Backups» (имя поля backup_limit, значение по умолчанию 3), а в расписании hourly_maintenance стоит задача delete_downloadable_backups. Она раз в час оставляет в private/backups только последние N комплектов и удаляет остальные. Следствие неочевидное: считается число комплектов, а не дни. Ночная копия с лимитом 3 даёт три ночи, а штатный запуск раз в 6 часов даёт всего 18 часов истории.
Из этого вытекает практический вывод: локальную глубину поднимайте осознанно, а долгую историю храните на втором сервере со своей политикой очистки. Для небольшой компании я обычно ставлю «Number of Backups» от 7 до 14, а на резервном сервере держу ежедневные копии за 30 дней и еженедельные за три месяца. Эти числа — мои рабочие ориентиры, а не правило Frappe: глубина зависит от того, как быстро вы замечаете ошибку в данных. Если бухгалтер обнаруживает расхождение раз в месяц, недельной глубины мало.
Шифрование включается в System Settings флажком «Encrypt Backups» (поле encrypt_backup). Тогда файлы получают в имени метку -enc, а при запуске команда напоминает записать ключ шифрования. Ключ лежит в site_config.json как encryption_key, и если потерять сервер вместе с ним, зашифрованная копия станет бесполезной. Храните ключ отдельно от копий, в менеджере паролей. При восстановлении ключ можно передать параметром --encryption-key. Помните и обратную сторону: копия site_config_backup.json содержит секреты, поэтому передавать её на сторонний сервер без шифрования нельзя.
Как восстановить сайт: bench restore, флаг --force и разбор типичных ошибок
Восстановление начинается с команды bench restore, и сразу предупреждаю: она перезаписывает базу сайта. В коде версии 15 видно, что восстановление идёт через пересоздание базы с параметром force, поэтому на живой сервер без предварительной копии текущего состояния запускать её нельзя. Сначала снимите свежий комплект того, что есть сейчас, даже если оно повреждено. Затем выполните:
cd ~/frappe-bench
bench --site erp.example.com restore \
sites/erp.example.com/private/backups/20261001_020000-erp_example_com-database.sql.gz \
--with-public-files sites/erp.example.com/private/backups/20261001_020000-erp_example_com-files.tar \
--with-private-files sites/erp.example.com/private/backups/20261001_020000-erp_example_com-private-files.tarИмя файла в примере условное, у вас будет своя метка времени. Документация Frappe отдельно оговаривает, что восстановить только файлы этой командой нельзя: файл базы обязателен.
На новом сервере порядок такой. Ставите ту же версию Frappe и ERPNext, что была на источнике, создаёте пустой сайт с тем же именем, копируете комплект и запускаете restore с путями ко всем трём файлам. Если версия новой установки ниже версии копии, команда предупредит о понижении и спросит подтверждение. Здесь и работает флаг --force: по описанию в коде он отключает проверки и предупреждения о downgrade. Это не лечебная кнопка. В форумных рецептах его советуют при ошибке Table 'tabDefaultValue' doesn't exist, но я проверил логику: перед загрузкой команда ищет в дампе таблицу __Auth, и --force лишь пропускает эту проверку, а повреждённый файл от этого не оживёт.
Поэтому при ошибках я иду по порядку, а не включаю force. Сначала проверяю целостность архива командой gzip -t файл.sql.gz. Потом смотрю, что запустил restore тот пользователь, у которого есть доступ к MariaDB, и что передан пароль root базы, если нужно создавать базу заново (параметры --db-root-username и --db-root-password). Затем сверяю версии: восстановление копии версии 15 на сервер с версией 14 невозможно без миграции. И только если проверка файла не прошла, а нужные данные все равно есть, я иду на осознанный риск с --force и затем внимательно проверяю результат.
| Что видно | Вероятная причина | Что делать |
|---|---|---|
Table 'tabDefaultValue' doesn't exist | Битый или неполный дамп, не те права на базу | gzip -t, проверить права MariaDB, взять другой комплект |
| Документы есть, вложения не открываются | Восстановили только базу | Повторить restore с --with-public-files и --with-private-files |
| Запрос подтверждения про downgrade | Версия копии новее установленной | Поставить ту же версию, а не обходить через --force |
| Нет стилей, 404 на CSS и JS | По сообщениям на форуме, не собраны ассеты после переноса | Выполнить bench build, перезапустить процессы, проверить в своей версии |
Как убедиться, что копия живая: пробное восстановление на стенде
Копия, которую ни разу не разворачивали, остаётся предположением. Единственная проверка, которой я верю, — пробное восстановление на отдельном стенде. Для малой компании хватает недорогой виртуальной машины с той же версией ERPNext. Порядок действий короткий: берёте последний комплект с резервного сервера, а не с рабочего, разворачиваете его на стенде и проверяете три вещи. Открывается ли список документов за последний день. Скачивается ли вложение из недавней заявки. Совпадает ли число записей в нескольких ключевых DocType с рабочей системой. Если одна из проверок не прошла, копия не считается рабочей, и я разбираюсь с причиной сразу.
Есть и сценарий проверки без отдельного стенда, он описан в нашей главе: удалить тестовый документ, выполнить restore и убедиться, что документ вернулся. Я использую его только на тестовом сайте, потому что на рабочем это та же перезапись базы. Подробнее про принципы проверки копий я писал в статье про тест восстановления бэкапа, а здесь важен конкретный набор проверок именно для ERPNext: открыть вложение, проверить печатную форму, зайти под обычной ролью и убедиться, что список проектов не пуст.
Регламент проверок я записываю в одну таблицу и вешаю на мониторинг. Ежедневно скрипт сверяет, что свежий комплект появился и его размер не упал против вчерашнего более чем на заданную долю. Раз в неделю проверяется, что копия дошла на второй сервер. Раз в месяц делается пробное восстановление с замером времени: это время и есть ваш реальный срок возврата к работе. Перед каждым обновлением версии снимается внеплановый полный комплект с вложениями. Команда bench update сама снимает копию сайтов перед обновлением, а флаг --no-backup, который это отключает, в описании прямо помечен как не рекомендуемый для production. Я его на рабочих серверах не использую.
Не забудьте про Docker. Если сайт работает в контейнерах, то команда docker compose down -v удалит именованные тома вместе с базой и файлами, это прямо описано в документации Docker к ключу -v. Перед любой работой с контейнерами убедитесь, что свежий комплект уже лежит вне тома. Я добавляю в регламент отдельную строку: «ключ -v не используем никогда на рабочем проекте».
Как «Электромонтаж Ритм» на 15 рабочих мест нашёл дыру в бэкапе до аварии
Разберу условный пример: подрядчик по электромонтажу «Электромонтаж Ритм», 15 рабочих мест, ERPNext в Docker на одном виртуальном сервере. Система стояла по схеме быстрой установки с готовым расписанием копий каждые шесть часов. Администратор был уверен, что копии есть: каталог private/backups пополнялся, размеры файлов выглядели правдоподобно. Числа ниже условные, они нужны для иллюстрации хода рассуждений, а не как данные реального проекта.
При первой плановой проверке копию развернули на тестовой машине. Документы открылись, проводки совпали, но в заявках на материалы не открывались сканы счетов и фотографии актов: ссылка есть, файла нет. Причина обнаружилась быстро: расписание запускало bench --site all backup без --with-files, поэтому за все месяцы работы вложения не копировались ни разу. На рабочем сервере к тому моменту накопилось около 9 ГБ вложений, и вся эта история существовала в единственном экземпляре. Дополнительно выяснилось, что поле «Number of Backups» оставалось равным 3, и глубина локальной истории составляла 18 часов.
Починили в три шага. Расписание заменили на ночное с --with-files, а лимит в System Settings подняли до 10. Задачей rsync комплект стал уезжать на второй сервер в другой площадке. Раз в месяц администратор запускает пробное восстановление и записывает время: на условном стенде оно заняло около 40 минут вместе с вложениями. Отдельным пунктом договорились, что перед каждым обновлением ERPNext снимается внеплановый полный комплект, а восстановление проверяет не тот, кто настраивал расписание, а второй сотрудник, чтобы ошибка не повторилась из-за привычки.
Чего этот регламент не закрывает
Любой логический дамп через bench backup сохраняет состояние на момент снятия, но не журнал изменений между копиями. Если копия ночная, а ошибку в данных сделали в 16:00, при возврате вы потеряете работу всего дня. Для малой компании это обычно приемлемо, но решение должно быть принятым осознанно: допустимую потерю нужно проговорить с директором до аварии, а не после. Если потеря дня недопустима, нужна другая схема: репликация MariaDB или копирование бинарных журналов, а это отдельный проект с отдельным администрированием.
Второе ограничение — размер. Для крупных баз дамп грузит сервер, а в нашей главе упомянуто, что на больших системах рекомендуют физические копии через Mariabackup. Я встречал на форумах рецепты, где для баз больше гигабайта восстанавливают дамп напрямую через клиент mysql, а не через bench restore. Эти советы относятся к старым версиям. Прежде чем на них опираться, проверьте их на своей версии и на тестовой копии. Для компании на 10–30 пользователей такой размер базы встречается редко, и обычный bench backup справляется.
Третье: копия сервера и копия данных — разные вещи. Комплект bench backup не восстановит операционную систему, настройки nginx, обратного прокси и сертификаты. Эти файлы нужно хранить отдельно, в репозитории конфигураций. Подробнее про общую стратегию копирования Linux-серверов — в материале про бэкап MySQL и MariaDB. И наконец, ответственность за проверку лежит на человеке. Ни одна команда не заменит живого сотрудника, который раз в месяц разворачивает копию и открывает вложение.
Частые вопросы
Какая команда делает резервную копию ERPNext?
Штатная команда: bench --site имя_сайта backup. По умолчанию она сохраняет только базу и копию site_config в каталог sites/имя_сайта/private/backups. Чтобы в копию попали вложения, добавьте флаг --with-files. Запускайте её от пользователя Linux, которому принадлежит каталог bench, иначе созданные файлы получат неправильного владельца и потом не читаются.
Как восстановить ERPNext из бэкапа?
Командой bench --site имя_сайта restore путь_к_sql.gz, дополнительно указав --with-public-files и --with-private-files с путями к tar-архивам. Команда перезаписывает базу сайта, поэтому перед запуском снимите копию текущего состояния. На новом сервере сначала поставьте ту же версию Frappe и ERPNext и создайте пустой сайт с тем же именем.
Сколько хранить бэкапы ERPNext и как часто делать?
Локальную глубину задаёт поле Number of Backups в System Settings, по умолчанию 3 комплекта, и считаются именно комплекты, а не дни. Для небольшой компании обычно хватает ночной копии, а вторую копию с историей 30 дней держат на другом сервере. Допустимую потерю данных согласуйте с руководителем заранее.
Почему после восстановления ERPNext пропали вложения?
Чаще всего сохранили только дамп базы. Вложения лежат в каталогах public/files и private/files, а не в базе, и попадают в копию только с флагом --with-files. Проверьте, есть ли рядом с архивом базы файлы files.tar и private-files.tar, и повторите восстановление с обоими параметрами.
Можно ли бэкапить ERPNext в Docker так же, как обычную установку?
Команды те же, но запускаются внутри контейнера backend, например docker compose -p erpnext exec backend bench --site all backup --with-files. Копии лежат в томе sites, поэтому их нужно выносить на другой сервер. Команда docker compose down -v удаляет тома с базой и файлами, не используйте её на рабочем проекте.
Источники
- Исходники Frappe v15: команды backup и restore — Проверены параметры backup (--with-files, --compress, --backup-path, --include/--exclude, --verbose) и restore (--with-public-files, --with-private-files, --force, --encryption-key), перезапись базы при restore, проверка таблицы __Auth. https://github.com/frappe/frappe/blob/version-15/frappe/commands/site.py
- Исходники Frappe v15: модуль backups и расписание — Проверены шаблоны имён файлов копий, поле backup_limit (по умолчанию 3) и задача delete_downloadable_backups в hourly_maintenance. https://github.com/frappe/frappe/blob/version-15/frappe/utils/backups.py и https://github.com/frappe/frappe/blob/version-15/frappe/desk/page/backups/backups.py
- Документация Frappe: bench backup и bench restore — Проверено: набор опций, восстановить только файлы без базы нельзя. https://docs.frappe.io/framework/user/en/bench/reference/backup и https://docs.frappe.io/framework/user/en/bench/reference/restore
- Репозиторий bench: bench setup backups — Проверено: задача cron каждые 6 часов с командой bench --verbose --site all backup, без --with-files. https://github.com/frappe/bench/blob/develop/bench/bench.py
- frappe_docker: стратегия бэкапа и compose.backup-cron — Проверено: пример cron с --with-files в документации и расписание @every 6h без файлов в overrides. https://github.com/frappe/frappe_docker/blob/main/docs/03-production/02-backup-strategy.md
- Документация Docker: docker compose down — Проверено: ключ -v удаляет именованные тома из секции volumes. https://docs.docker.com/reference/cli/docker/compose/down/



