Бэкап ERPNext: bench backup, restore и проверка копии
АйТи Фреш
Linux, Docker и DevOps

Бэкапы ERPNext: bench backup, восстановление и проверка, что копия живая

Автор: , директор ООО «АйТи-Фреш» · · ~17 мин чтения
Две папки архива: полная с чертежами и пустая, лупа и штамп проверки — резервная копия ERPNext
Копия без вложений похожа на папку с пустыми обложками.

Бэкап 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: набор флагов с годами менялся.

Таблица комплекта копии ERPNext: база и настройки копируются по умолчанию, вложения требуют отдельного флага
Вложения лежат не в базе, а на диске. Открыть крупно

Как сделать бэкап командой 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 содержит секреты, поэтому передавать её на сторонний сервер без шифрования нельзя.

Одна копия на том же диске — это не резервная копия, а удобство при откате. Держите хотя бы одну копию на другом сервере и сверяйте её дату раз в неделю.
Столбцы глубины истории копий при разных расписаниях и лимитах: 18, 72 и 240 часов
Лимит считает комплекты, поэтому частые копии быстро вытесняют старые. Открыть крупно

Как восстановить сайт: 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 снимается внеплановый полный комплект, а восстановление проверяет не тот, кто настраивал расписание, а второй сотрудник, чтобы ошибка не повторилась из-за привычки.

Четыре показателя примера подрядчика: 15 рабочих мест, 9 ГБ вложений, 18 часов истории, 40 минут восстановления
Цифры придуманы для иллюстрации хода рассуждений. Открыть крупно

Чего этот регламент не закрывает

Любой логический дамп через 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 удаляет тома с базой и файлами, не используйте её на рабочем проекте.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи