Чем грозит «поставили и забыли»: три отказа из нашей практики
Меня зовут Евгений Семёнов, я техдиректор ITfresh. В предыдущих статьях серии мы разобрали, что умеет Odoo Community как ERP, развернули её на сервере и прикрутили российскую локализацию. Сегодня — про то, о чём интеграторы обычно молчат: что происходит с инсталляцией через полгода-год после запуска, если её никто не обслуживает.
Когда в Odoo живут только контакты и сделки — потеря данных неприятна. Когда там склад, закупки, производственные заказы и вся цепочка «заказ клиента → резерв → отгрузка → счёт» — простой ERP на день останавливает отгрузки физически: кладовщик не знает, что собирать. Три реальных отказа, с которыми к нам приходили на сопровождение:
- Диск съел filestore. Оптовик, около 30 рабочих мест. К каждой приёмке сканируют накладную и УПД, к каждой карточке товара — 3–5 фотографий, плюс ЭДО-архив PDF. Каталог вложений вырос примерно с 5 до 70 ГБ за полтора года, диск на 100 ГБ кончился, PostgreSQL не смог записать WAL и остановился. ERP лежала полдня, отгрузки оформляли на бумаге.
- База погибла, бэкапа не было. «Хостер же делает копии» — делал, снапшот раз в неделю, целостность которого никто не проверял. После сбоя диска восстановился снапшот шестидневной давности: минус шесть дней складских перемещений, приёмок и счетов. Пересобирали остатки инвентаризацией всего склада.
- Открытый менеджер баз данных. Страница
/web/database/managerдоступна из интернета, мастер-пароль — дефолтныйadmin. Любой, кто дошёл до неё сканером, может скачать полный дамп базы со всеми ценами и контрагентами или просто удалить базу. Боты перебирают типовые пути Odoo постоянно — мы видим это в логах каждого клиентского сервера.
Вывод из всех трёх историй один: Odoo — надёжный софт, но это продакшен-сервис уровня почты или 1С. Ему нужен регламент: что делаем каждый день, что раз в неделю, что раз в месяц и что при выходе нового релиза. Ниже — регламент, по которому мы в ITfresh сопровождаем клиентские инсталляции. Забирайте и пользуйтесь.
Бэкапы правильно: три слоя вместо одного
Слой 1–3: база, filestore, снапшот VM
Главная особенность Odoo, о которую спотыкаются, — вложения не лежат в базе данных. PDF накладных, фото товаров, отчёты о приёмке хранятся файлами в каталоге filestore (обычно /var/lib/odoo/.local/share/Odoo/filestore/имя_базы или /var/lib/odoo/filestore в Docker-томе). Дамп PostgreSQL без filestore — это ERP, в которой документы открываются, а приложенные к ним файлы дают 404.
Грабля номер один: pg_dump не содержит вложений. Проверьте прямо сейчас, попадает ли каталог filestore в вашу схему резервного копирования. В ERP-инсталляции со складом он растёт быстрее базы в разы — именно он в итоге занимает десятки гигабайт.
Поэтому слоёв три, и каждый закрывает свой класс аварий:
- pg_dump ежедневно, ночью, в custom-формате (
-Fc— сжатие и выборочное восстановление). Ротация: 14 суточных копий на самом сервере, 30 суток на внешнем хранилище. Закрывает логические аварии: кривой модуль, ошибочное массовое удаление, неудачная миграция. - rsync каталога filestore той же ночью. Мы гоним его инкрементально с жёсткими ссылками (
--link-dest): каждая ночная копия выглядит полной, а места ест только на новые файлы — для 70-гигабайтного filestore это минуты, а не часы. - Снапшот виртуальной машины еженедельно плюс обязательно перед каждым обновлением. Закрывает аварии уровня системы: погибшая ОС, неудачный апгрейд пакетов, шифровальщик. Снапшот — не замена дампам: он один, он на той же площадке и его нельзя открыть выборочно.

Скрипт: ночной бэкап с выгрузкой на внешнюю площадку
Копия, лежащая рядом с сервером, погибнет вместе с ним. Поэтому финальный шаг скрипта — выгрузка на независимое хранилище: S3-совместимый бакет российского провайдера или FTP на площадке в другом ЦОД (у нас — собственные серверы в ЦОД МТС). Рабочий скелет, который мы кладём клиентам в cron:
#!/bin/bash
set -e
DB="erp_prod"
FS="/var/lib/odoo/.local/share/Odoo/filestore/$DB"
DST="/backup/odoo"
TODAY=$(date +%F)
YESTERDAY=$(date -d yesterday +%F)
# 1. Дамп базы в custom-формате
mkdir -p "$DST/$TODAY"
sudo -u postgres pg_dump -Fc "$DB" -f "$DST/$TODAY/$DB.dump"
# 2. Инкрементальная копия filestore (hardlink на вчерашнюю)
rsync -a --delete --link-dest="$DST/$YESTERDAY/filestore" \
"$FS/" "$DST/$TODAY/filestore/"
# 3. Выгрузка наружу: rclone в S3-совместимый бакет
rclone sync "$DST/$TODAY" "s3-backup:erp-backup/$TODAY" \
--transfers 8 --s3-chunk-size 64M
# 4. Ротация: 14 суток локально, 30 — в бакете
find "$DST" -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
rclone delete "s3-backup:erp-backup" --min-age 30d --rmdirs
# 5. Отчёт в мониторинг (пустой файл-маркер с датой)
touch /var/run/odoo-backup-ok
Маркер из последней строки проверяет Zabbix: если файлу больше 26 часов — алерт «бэкап не отработал». Молча сломавшийся бэкап хуже отсутствующего: он даёт ложное чувство защищённости.
Бэкап не существует, пока не восстановлен
Раз в месяц мы разворачиваем последний дамп на стейджинг-машине и смотрим не «файл скачался», а «ERP работает»: база поднялась, остатки по складам совпадают с продом на момент дампа, вложения к последним приёмкам открываются, отчёт по закупкам строится. На стейджинге обязательно выполняем нейтрализацию базы (о команде — в разделе про аварии), чтобы копия не начала слать письма контрагентам и гонять планировщики закупок. Пятнадцать минут в месяц — и в день настоящей аварии вы восстанавливаетесь по отработанной процедуре, а не изобретаете её под крики из отдела продаж.
Обновления: минорные — рутина, мажорные — мини-проект
Минорные: окно 20 минут по чек-листу
Внутри мажорной версии Odoo регулярно выпускает исправления — багфиксы и заплатки безопасности без изменения схемы данных. Для установки из пакетов это apt update && apt upgrade odoo, для Docker — свежий образ тега своей версии. Порядок у нас жёсткий и всегда одинаковый:
- Снапшот VM (минута, но именно он превращает неудачное обновление в нестрашное).
- Внеочередной запуск бэкап-скрипта.
docker compose pull && docker compose up -d— или apt-обновление с рестартом службы.- Смоук-тест три минуты: вход, создать черновик заказа, открыть складские остатки, проверить лог на трейсбеки.
Окно — вечер буднего дня после отгрузок, простой две-три минуты на рестарт. Делаем раз в 3–4 недели; заплатки безопасности — вне очереди.
Мажорные (18 → 19): почему это отдельный проект
Мажорный релиз меняет схему базы, и просто «подсунуть» базу 18-й версии коду 19-й нельзя. Для Community-редакции миграцию делают через OpenUpgrade — открытый инструмент сообщества OCA, который прогоняет базу через скрипты преобразования версия-в-версию. И тут ERP-инсталляция сложнее «голой» CRM на порядок:
- каждый сторонний модуль OCA (склад, закупки, российская локализация, коннектор с 1С) должен существовать в ветке под новую версию — а порты модулей выходят через месяцы после релиза;
- кастомные поля и правила пополнения запасов надо проверять руками: изменения в модели склада между версиями регулярно ломают доработки;
- прогон на стейджинге обязателен: полная копия базы + filestore, миграция, неделя-две параллельной проверки ключевых сценариев людьми, которые в системе работают.
Бюджетируйте мажорный апгрейд как проект на 2–4 недели календарного времени, а не как «обновимся в субботу».
Наша политика версий
Odoo выпускает мажорный релиз раз в год осенью (19-я вышла осенью 2025-го) и поддерживает три последних мажора — сейчас это 17, 18 и 19. Наше правило: прод отстаёт от свежего мажора на 3–6 месяцев. За это время экосистема OCA догоняет релиз портами модулей, а первые минорные заплатки выгребают детские болезни. Обновляться реже, чем раз в три года, тоже нельзя: версия выпадает из поддержки, перестаёт получать заплатки безопасности, а прыжок через два-три мажора по цепочке OpenUpgrade мучительнее трёх плановых апгрейдов вместе взятых.
Безопасность инсталляции: закрываем то, что ломают ботами
ERP хранит закупочные цены, маржу, базу поставщиков и клиентов — то, за чем приходят целенаправленно. Минимальный обязательный набор, который мы применяем на каждом сервере:
list_db = Falseи сильныйadmin_passwdвodoo.conf. Первое прячет список баз и сам менеджер БД, второе — мастер-пароль на операции с базами: 24+ случайных символов из менеджера паролей, а не название компании.- Менеджер баз — только со своих IP. Даже со включённым
list_db = Falseмы дополнительно режем/web/database/на уровне nginx:allowдля офисного IP и VPN,deny allдля остальных. Двойной забор дешевле одного инцидента. - nginx + TLS с автопродлением. Odoo слушает 8069 только на 127.0.0.1, наружу — nginx с сертификатом Let's Encrypt. Грабля, на которую наступают постоянно: certbot в режиме standalone пытается сам занять порт 80, который уже занят nginx, — и продление молча падает. Используйте
certbot --nginx(авторизация через работающий веб-сервер) и проверяйтеcertbot renew --dry-run. - fail2ban на страницу логина. Odoo пишет неудачные входы в свой лог — по этой строке fail2ban банит перебор паролей на уровне фаервола. Правило простое: 5 промахов за 10 минут — бан на час.
- 2FA для пользователей. Встроенная TOTP-двухфакторка включается в настройках без сторонних модулей. Обязательна для админов и всех, кто ходит в ERP извне офиса.
- Права по группам, а не «всем всё». Кладовщику — склад без закупочных цен, менеджеру — продажи без настроек, бухгалтеру — учёт. Раз в квартал сверяем список активных пользователей с кадрами: учётки уволенных — самый недооценённый вектор утечки.
# /etc/fail2ban/filter.d/odoo-login.conf
[Definition]
failregex = ^.*\sodoo\.addons\.base\.models\.res_users:\s+Login failed for db:.* login:.* from <HOST>
# /etc/fail2ban/jail.d/odoo.conf
[odoo-login]
enabled = true
port = http,https
filter = odoo-login
logpath = /var/log/odoo/odoo-server.log
maxretry = 5
findtime = 600
bantime = 3600
Мониторинг и логи: узнать о проблеме раньше клиента
Цель мониторинга — чтобы о проблеме первым узнал инженер, а не кладовщик, у которого не открывается приёмка. Мы вешаем на каждую инсталляцию связку «Zabbix + Telegram-алерты» (подойдёт и любой uptime-сервис, если Zabbix для вас избыточен) и смотрим пять вещей:
- Доступность и время ответа HTTP. Веб-сценарий дёргает
/web/loginраз в минуту. Алерт не только на «лежит», но и на деградацию: ответ дольше 3 секунд три проверки подряд — значит, база пухнет или воркеры захлебнулись, и через неделю это станет простоем. - Место на диске — с прогнозом. Не «осталось меньше 10%», а «при текущей скорости роста filestore диск кончится через N дней». Zabbix умеет предиктивные триггеры из коробки; история с оптовиком из первого раздела не случилась бы.
- Длинные транзакции PostgreSQL. Зависшая транзакция блокирует autovacuum и коллег: складской отчёт, который держит транзакцию 20 минут, тормозит всю ERP. Запрос для агента мониторинга:
SELECT pid, now() - xact_start AS dur, state, query FROM pg_stat_activity WHERE xact_start IS NOT NULL AND now() - xact_start > interval '5 minutes'; - Ошибки cron-задач в логе Odoo. Самое коварное в ERP: планировщики падают беззвучно. Упавшее правило пополнения запасов (reordering rules) не создаёт черновики закупок — и об этом узнают через две недели, когда склад пуст, а поставка едет 30 дней. Грепаем лог на
ERRORиTracebackрядом сodoo.addons.base.models.ir_cron— любое совпадение уходит алертом. - Маркер бэкапа — тот самый файл из скрипта выше: старше 26 часов — алерт.
Практика: алертов должно быть мало и все — по делу. Если канал в Telegram сыпет по 20 сообщений в день, его перестают читать за неделю, и настоящая авария тонет в шуме. Начните с пяти проверок выше — они закрывают 90% реальных инцидентов Odoo.
Производительность в бою: когда отчёт по остаткам строится 30 секунд
Деградация ERP наступает не скачком, а ползком: через год работы отчёт по остаткам, который открывался за 2 секунды, строится 30. Таблицы складских перемещений (stock_move, stock_move_line) — самые быстрорастущие в базе: каждая отгрузка и приёмка рождает десятки строк. Что мы делаем в порядке приоритета:
- Настраиваем autovacuum агрессивнее дефолта для горячих таблиц. Стандартные пороги PostgreSQL рассчитаны на средние нагрузки; на таблице в несколько миллионов строк перемещений мёртвые кортежи копятся быстрее, чем дефолтный autovacuum их вычищает, — раздувается и таблица, и индексы. Снижаем
autovacuum_vacuum_scale_factorдо 0.02 точечно черезALTER TABLE, а после массовых чисток запускаем ручнойVACUUM ANALYZEв ночное окно. - Индексы на кастомные поля. Каждое добавленное поле, по которому фильтруют список или строят отчёт («наш артикул поставщика», «зона склада»), должно иметь индекс — в Odoo это флаг
index=Trueна поле. Ищем кандидатов поpg_stat_statements: медленные запросы с Seq Scan по большим таблицам. - Лимиты воркеров по формуле. Рабочая отправная точка —
workers = CPU×2 + 1, при 4 ядрах это 9; плюсlimit_memory_soft/limit_memory_hard, чтобы разжиревший от тяжёлого отчёта воркер перезапускался сам, а не утаскивал сервер в своп. Мало воркеров — очередь запросов, много — драка за память.
Когда пора переезжать с 4 на 8 GB RAM — не по ощущениям, а по метрикам-триггерам: своп используется постоянно (не пики, а фон), в логе появляются перезапуски воркеров по достижении limit_memory_hard чаще нескольких раз в день, PostgreSQL не может держать рабочий набор в кэше (shared_buffers упирается, попадание в кэш падает ниже ~95%). Для типовой инсталляции на 20–40 активных пользователей со складом 8 GB — комфортная норма, 4 GB — стартовый минимум, который заканчивается через год роста базы.
Регламент сопровождения ITfresh: таблица, которую можно забрать
Всё перечисленное складывается в один лист. Это наш внутренний шаблон регламента для клиентских Odoo — вычеркните лишнее, впишите свои имена серверов и пользуйтесь:
| Периодичность | Операции | Кто/как |
|---|---|---|
| Ежедневно | pg_dump + rsync filestore + выгрузка на внешнее хранилище; контроль маркера бэкапа; разбор алертов мониторинга; просмотр лога на новые ERROR/Traceback | Автоматика; инженер — только по алерту |
| Еженедельно | Снапшот VM; обновления безопасности ОС (unattended-upgrades + контроль); динамика диска и роста filestore; проверка успешности cron-задач Odoo (закупки, рассылки, курсы валют); certbot renew --dry-run | Инженер, 20–30 минут |
| Ежемесячно | Тестовое восстановление дампа на стейджинг с нейтрализацией; минорное обновление Odoo по чек-листу; VACUUM ANALYZE горячих таблиц и просмотр pg_stat_statements; аудит пользователей и прав (уволенные, лишние группы); просмотр банов fail2ban | Инженер, 2–3 часа |
| На мажорный релиз | Ревизия совместимости OCA-модулей и кастомизаций; полный клон прод-базы на стейджинг; миграция OpenUpgrade; 1–2 недели параллельной проверки ключевыми пользователями; плановое окно переключения с точкой отката (снапшот + дамп) | Проект 2–4 недели, через 3–6 мес после релиза |

Суммарно живого времени инженера — порядка 4–6 часов в месяц на инсталляцию. Это и есть цена того, чтобы ERP со складом «просто работала» годами.
Что делать при аварии: план восстановления с командами
Сценарий: сервер погиб (диск, гипервизор, шифровальщик — неважно). Целевой RTO для бизнеса до 50 рабочих мест — 2–4 часа, RPO — сутки (ночной бэкап). Порядок действий, отработанный на ежемесячных тест-ресторах:
- Оценка (10 минут). Если жив гипервизор — откат на снапшот и накат свежего дампа поверх быстрее всего. Если площадка потеряна целиком — поднимаем новую VM из шаблона (Odoo + PostgreSQL + nginx у нас в стандартном образе).
- Забираем последний бэкап с внешнего хранилища и восстанавливаем базу:
# забрать последний комплект из бакета
rclone copy "s3-backup:erp-backup/2026-07-29" /restore/
# пересоздать базу и накатить дамп
sudo -u postgres dropdb --if-exists erp_prod
sudo -u postgres createdb -O odoo erp_prod
sudo -u postgres pg_restore -d erp_prod --no-owner --role=odoo \
-j 4 /restore/erp_prod.dump
# вернуть filestore на место и раздать права
mkdir -p /var/lib/odoo/.local/share/Odoo/filestore
cp -a /restore/filestore \
/var/lib/odoo/.local/share/Odoo/filestore/erp_prod
chown -R odoo:odoo /var/lib/odoo
systemctl start odoo
- Проверка перед открытием доступа: вход админом, складские остатки на вчерашний вечер, вложения к последним приёмкам открываются, cron-задачи в статусе «готово к запуску».
- Переключение DNS/проброса на новый сервер, объявление пользователям: «работаем, данные на утро, документы за сегодня вносим повторно».
Отдельно — если вы поднимаете копию не вместо прода, а рядом с ним (стейджинг, разбор инцидента): её обязательно нейтрализуют, иначе копия начнёт жить как боевая — рассылать письма клиентам и плодить закупки по правилам пополнения. В Odoo для этого есть штатная команда:
# отключить в копии почтовые серверы, планировщики и внешние вызовы
odoo-bin neutralize -d erp_staging
Запомните главное: RTO в 2–4 часа достижим только потому, что процедура прогонялась ежемесячно. Первое в жизни восстановление, выполняемое в панике по статье из интернета, занимает день-два — проверено чужими авариями, которые мы разгребали.
Если держать этот регламент самим некому — мы возьмём вашу Odoo на сопровождение: бэкапы с ежемесячным тест-рестором, обновления, мониторинг с реакцией и восстановление при авариях на наших серверах в ЦОД МТС. Напишите в Telegram @ITfresh_Boss или позвоните +7 903 729-62-41 — за 30 минут разговора скажем честно, что у вас уже хорошо, а что грозит историей из первого раздела.

Оставить комментарий