Flatcurve в mailcow: включаем без перегрузки сервера
АйТи Фреш
Linux, Docker и DevOps

Включаю Flatcurve в mailcow и не кладу сервер: расчёт CPU и памяти под индексацию

Автор: , директор ООО «АйТи-Фреш» · · ~15 мин чтения
Настройка Flatcurve в mailcow с контролем CPU и памяти, чтобы индексация не перегрузила небольшой сервер
Flatcurve управляем по ресурсам — вопрос не «включать или нет», а с какими лимитами.

Документация mailcow честно предупреждает: индексация Flatcurve может нагрузить систему вплоть до зависаний. На небольшом VPS это не абстрактный риск, а реальный повод бояться включать полнотекстовый поиск вообще. Разбираю, как посчитать FTS_PROCS и FTS_HEAP под конкретное железо и что чаще всего делают не так при первом включении.

Кейс: маленький сервер и страх перед словом «индексация»

«Синема Гранд» — кинотеатр на 18 рабочих мест, почта на mailcow: договоры с кинопрокатчиками, афиши и постеры во вложениях, переписка с прокатчиками по расписанию сеансов. Мы ведём их корпоративную почту, сервер у них скромный — 4 vCPU и 8 ГБ RAM, ровно под текущую нагрузку, без запаса на эксперименты. После обновления до 2025-01 в mailcow.conf появился блок FTS-параметров, а поиск по телу писем в SOGo стал отвечать по минуте и дольше — предсказуемо: движок сменился с Solr на Flatcurve, update.sh дописал в конфиг SKIP_FTS=y, и Dovecot без индекса искал перебором всех писем.

Первая реакция администратора клиента была разумной осторожностью: он прочитал в документации фразу про возможные пиковые нагрузки CPU при индексации и решил вообще не трогать FTS, лишь бы не положить единственный сервер компании в рабочее время, когда идёт согласование расписания сеансов с прокатчиками. Это правильный инстинкт, но неполное решение — Flatcurve специально спроектирован так, чтобы быть управляемым по ресурсам, а не как всё-или-ничего. Вопрос не 'включать или нет', а 'с какими лимитами включать под конкретное железо'.

Ниже — то, как я считаю эти лимиты для клиентских серверов, на примере именно этой машины: 4 vCPU, 8 ГБ RAM, почта на 18 ящиков без экстремальных объёмов, но с тяжёлыми вложениями (афиши в высоком разрешении, PDF-макеты).

Прежде чем включать FTS на проде, посмотрите текущую загрузку сервера в рабочие часы: `docker stats --no-stream`. Если CPU и так под 70-80 % на пике — начинайте с самых консервативных настроек, а не с рекомендованных «по умолчанию для мощных систем».

Почему дефолты такие скромные — и что это значит для вас

Официальная документация формулирует это прямо: mailcow задаёт низкие значения по умолчанию для нового FTS-движка, чтобы обеспечить работоспособность даже на слабых системах, и только на более мощных системах рекомендует их увеличивать. По умолчанию FTS_PROCS (число одновременных процессов индексации) равно 1, а FTS_HEAP (память на каждый процесс индексации) — 128 МБ. Это не заниженные для галочки значения, а сознательно консервативная настройка, рассчитанная на самый слабый сценарий из возможных. Под капотом entrypoint-скрипт dovecot-mailcow при старте переписывает в fts.conf блок service indexer-worker: FTS_PROCS становится process_limit, а FTS_HEAP — vsz_limit в мегабайтах. И одна ловушка, которую я нашёл в исходниках: если строк FTS_PROCS/FTS_HEAP в mailcow.conf нет вовсе, docker-compose.yml подставит свои запасные значения — 3 процесса по 512 МБ, а это уже совсем не «скромно». Поэтому перед включением я всегда проверяю grep -E 'FTS_' mailcow.conf, что обе строки на месте.

Ключевая деталь про сам процесс индексации, которую стоит понимать перед тем, как крутить любые лимиты: каждый индексирующий процесс однопоточный и способен полностью занять одно ядро CPU. Документация прямо предупреждает — системы с малым числом ядер должны использовать меньшее число процессов, чтобы не перегрузить систему целиком. Это значит, что FTS_PROCS=4 на четырёхъядерной машине — не «использовать все ресурсы эффективно», а «отдать индексации все ядра сразу и оставить основным сервисам mailcow (Postfix, Dovecot, Rspamd, MySQL) конкурировать за то, что останется».

Для «Синема Гранд» это означало на старте одно: не спешить поднимать значения выше дефолтных, пока не увидим реальную картину нагрузки на первой индексации. Дефолт в 1 процесс и 128 МБ гарантированно не положит четырёхъядерный сервер с 8 ГБ RAM — вопрос был не в безопасности первого запуска, а в том, сколько времени первичная индексация займёт при таком консервативном лимите, и стоит ли аккуратно поднять значения после первого наблюдения.

Не путайте FTS_HEAP с памятью на весь механизм поиска — это лимит на каждый отдельный процесс индексации. При FTS_PROCS=2 и FTS_HEAP=256 МБ потенциальный пик — около 512 МБ, а не 256.
Сравнение дефолтных значений FTS_PROCS и FTS_HEAP в mailcow с рекомендованными для мощных серверов
Дефолты сознательно занижены — поднимать их нужно осознанно, а не автоматически.

Как включить правильно: одна строка в конфиге и одна ловушка с перезапуском

Формально включение — это одна правка: в mailcow.conf меняете SKIP_FTS=y на SKIP_FTS=n, затем применяете:

# mailcow.conf
SKIP_FTS=n

docker compose up -d

Здесь есть задокументированная сообществом ловушка, из-за которой администраторы решают, что «включение не сработало», хотя формально всё сделали правильно. В обсуждении «Flatcurve indexing does not seem to work» на официальном форуме администратор поменял SKIP_FTS=n, но вместо docker compose up -d просто перезагрузил хост целиком (попутно обновлял ядро Linux) — и FTS-плагин так и не подключился: doveadm index -A '*' возвращался мгновенно, каталогов fts-flatcurve не появилось. Механика простая: переменные из mailcow.conf попадают в контейнер только при его создании, а список плагинов Dovecot (fts fts_flatcurve) entrypoint-скрипт dovecot-mailcow пишет при старте по значению SKIP_FTS внутри контейнера. Перезагрузка хоста и docker compose restart запускают старые контейнеры со старыми переменными. Помогло пересоздание стека — автор проверил это и на второй своей инсталляции; docker compose up -d тоже пересоздаёт контейнеры с изменившимися переменными, а down + up -d — самый надёжный вариант, если сомневаетесь.

docker compose down
docker compose up -d

Ответ на форуме был коротким: перезагрузка ничего не меняет в конфигурации docker compose. Контейнеры после reboot поднимаются, но это те же самые контейнеры с теми же переменными — новое значение SKIP_FTS в файлы mail_plugins попадает только после пересоздания dovecot-mailcow. Для «Синема Гранд» я сразу использовал down/up -d, чтобы не наступить на эти же грабли — и первая же проверка doveadm index -u user@domain '*' прошла штатно. Похожие ловушки «формально применили настройку, а движок её не подхватил» я подробно разбирал и для других почтовых сбоев — например, в регламенте эксплуатации mailcow есть отдельный чек-лист именно на такие случаи после любого изменения переменных в mailcow.conf.

Если после включения FTS доступна команда doveadm index, но она не создаёт файлов в fts-flatcurve, — не тратьте время на диагностику сети или прав. Сначала проверьте `docker compose exec dovecot-mailcow cat /etc/dovecot/mail_plugins` — если там нет `fts_flatcurve`, контейнер не пересоздан: выполните `docker compose up -d` (или `down` + `up -d`).
Схема правильного включения Flatcurve в mailcow: SKIP_FTS=n плюс полный docker compose down и up -d, простой reboot не помогает
Перезагрузка хоста не пересоздаёт конфигурацию FTS-плагина — нужен полный цикл down/up.

Расчёт FTS_PROCS под число ядер CPU

Правило из документации звучит просто — закладывайте под индексацию примерно половину потоков CPU системы, а при нечётном числе потоков округляйте вниз, чтобы гарантированно оставить достаточно ресурсов основным сервисам. Отдельно и однозначно: системы с одним или двумя ядрами документация рекомендует держать с полностью отключённым FTS — не с минимальным FTS_PROCS=1, а именно выключенным, потому что на таком железе даже один занятый под индексацию поток CPU — это половина или вся вычислительная мощность сервера.

На практике это выглядит как простая таблица, которую я использую как отправную точку в разговоре с клиентом перед первым включением:

Ядра/потоки CPUFTS_PROCSКомментарий
1-2FTS не включатьдокументация: отключить FTS полностью
31половина, округлённая вниз
4 (наш случай)2ровно половина
63половина
84половина

У «Синема Гранд» сервер с 4 vCPU, и по этому правилу отправная точка — FTS_PROCS=2. Я сознательно не стал брать максимум, разрешённый формулой, для первого запуска: начал с FTS_PROCS=1 на время первичной полной индексации архива (несколько месяцев переписки с прокатчиками), посмотрел на docker stats во время процесса, и только после спокойной первой индексации поднял значение до FTS_PROCS=2 для последующей штатной фоновой работы.

Не считайте виртуальные потоки Hyper-Threading как полноценные ядра при планировании FTS_PROCS на пограничных конфигурациях (2-4 vCPU) — под нагрузкой они ведут себя слабее физических ядер.

Расчёт FTS_HEAP под объём RAM

С памятью документация даёт более развёрнутую вилку. Идеальная рекомендация — 512 МБ на процесс индексации, если позволяет объём RAM. Но для систем с оперативной памятью менее 8 ГБ рекомендация другая: держаться 128 МБ, при необходимости поднимать до 256 МБ, и при этом сокращать число процессов, чтобы избежать ошибок нехватки памяти (OOM). Отдельно документация уточняет важный нюанс поведения при нехватке памяти: Dovecot не падает жёстко, если памяти конкретному воркеру не хватает, — он продолжает работать, но заметно медленнее. Это не оправдание экономить на памяти бездумно, но полезное знание для оценки риска: перегрузка по FTS_HEAP скорее посадит скорость индексации, чем весь почтовый сервис целиком.

8 ГБ RAM у «Синема Гранд» формально на границе рекомендации «менее 8 ГБ» — фактически впритык к порогу, а не с большим запасом, потому что эти же 8 ГБ несут MySQL, Redis, Rspamd, Dovecot, Postfix и SOGo одновременно, и FTS — не единственный потребитель памяти на машине. Поэтому я взял нижнюю границу вилки: FTS_PROCS=2 и FTS_HEAP=128, то есть теоретический пик памяти под индексацию — около 256 МБ, что для этой машины комфортно даже в моменты одновременной пиковой нагрузки других сервисов.

# mailcow.conf, итоговые значения для 4 vCPU / 8 ГБ RAM
SKIP_FTS=n
FTS_PROCS=2
FTS_HEAP=128

docker compose down
docker compose up -d

Если бы у клиента было, например, 16 ГБ RAM с тем же числом ядер, я бы поднял FTS_HEAP до 256-512 МБ, оставив FTS_PROCS привязанным к числу ядер, а не к памяти — по документации это два независимых лимита, и увеличивать имеет смысл тот, где реально есть запас ресурса именно этого типа, а не оба сразу «на всякий случай». Тот же принцип разделения лимитов по типу ресурса я использую и за пределами mailcow — например, при ограничении памяти через systemd для других сервисов на том же сервере, я разбирал это отдельно в тексте про MemoryHigh и MemoryMax: там тоже важно не путать мягкий лимит с жёстким и не резать оба сразу вслепую.

На границе рекомендации «менее 8 ГБ» (то есть ровно 8 ГБ, как у «Синема Гранд») я всегда беру нижнюю, а не верхнюю границу вилки — сервер редко несёт только mailcow и никогда не бывает лишнего запаса памяти в моменты пиковой почтовой нагрузки.
Таблица подбора значений FTS_PROCS и FTS_HEAP под три типовых профиля сервера mailcow
Формула одна — половина ядер и вилка по памяти, но итоговые числа у каждого сервера свои.

Первая индексация: что смотреть и когда поднимать лимиты

Запуск первичной индексации я всегда делаю вручную и под наблюдением, а не полагаюсь на автоматическую фоновую индексацию по мере накопления новых писем — для архива в несколько месяцев переписки это заняло бы недели. Команда для одного пользователя за раз, чтобы видеть прогресс и иметь возможность остановиться:

docker compose exec dovecot-mailcow doveadm index -u user@domain '*'

Во время первого прогона у «Синема Гранд» я держал открытым второе окно с docker stats dovecot-mailcow mysql-mailcow — смотрел не на пиковые всплески CPU (они ожидаемы и нормальны для однопоточного процесса индексации), а на устойчивость памяти и на то, не начинает ли расти память MySQL параллельно, что было бы признаком того, что индексация конкурирует с рабочей нагрузкой почты, а не идёт в свободное время. На FTS_PROCS=2 / FTS_HEAP=128 полная индексация 18 ящиков с архивом переписки заняла около 40 минут в нерабочее время вечером, без заметного влияния на отправку и приём текущей почты.

После недели стабильной работы в фоновом режиме — новые письма индексируются пачками при поступлении, без ручного вмешательства — я поднял FTS_HEAP до 256 у клиента, посмотрев, что запас памяти на сервере в рабочие часы остаётся комфортным даже на пиках. Заодно учтите, что в mailcow по расписанию (через ofelia, каждую ночь в 00:00) запускается optimize-fts.sh в контейнере dovecot-mailcow — оптимизация индексов Flatcurve, которая тоже даёт небольшую ночную нагрузку; я не планирую на это время бэкапы и тяжёлые задачи. Поднимать лимиты стоит именно так: маленькими шагами, с наблюдением, а не сразу на максимум, рекомендованный документацией для абстрактной «мощной системы», которой конкретный сервер клиента может и не быть.

Если сервер уже разделён по ресурсам через cgroups — например, mailcow живёт рядом с другими сервисами и у каждого свой лимит — стоит заранее прикинуть, не упрётся ли индексация в общий потолок раньше, чем в собственные FTS_PROCS/FTS_HEAP; я разбирал механику таких лимитов отдельно в материале про Linux cgroups v2. Для «Синема Гранд» такого разделения не было — mailcow стоял на выделенной под него машине безраздельно, поэтому единственными реальными потолками были параметры самого Flatcurve.

Если во время первой индексации сервер ощутимо тормозит для пользователей — не ждите, остановите процесс (`Ctrl+C` в сессии docker compose exec; уже проиндексированное останется, а следующий `doveadm index` доиндексирует недостающее) и снизьте FTS_PROCS перед повторной попыткой.

Частые вопросы

Можно ли включить Flatcurve на небольшом VPS без риска перегрузки?

Да, если выставить консервативные значения FTS_PROCS и FTS_HEAP под конкретное железо, а не оставлять дефолты или сразу ставить рекомендованные для мощных систем значения. Документация специально задаёт низкие дефолты (FTS_PROCS=1, FTS_HEAP=128 МБ) именно для слабых систем — с них и стоит начинать, поднимая лимиты постепенно после наблюдения за нагрузкой.

Как рассчитать FTS_PROCS под число ядер сервера?

Документация рекомендует закладывать под индексацию примерно половину потоков CPU системы, округляя нечётное число в меньшую сторону. Для серверов с 1-2 ядрами рекомендуется не включать FTS вообще — индексирующий процесс однопоточный и займёт значительную долю всей вычислительной мощности.

Сколько памяти выделять на FTS_HEAP?

Идеал — 512 МБ на процесс индексации при достаточном запасе RAM. Для систем с менее чем 8 ГБ RAM документация рекомендует держаться 128-256 МБ и одновременно сокращать число процессов (FTS_PROCS), чтобы не допустить ошибок нехватки памяти.

Я поменял SKIP_FTS и перезагрузил сервер, но поиск всё равно не работает — почему?

Перезагрузка хоста запускает те же контейнеры со старыми переменными и не пересоздаёт конфигурацию FTS-плагина в mail_plugins Dovecot — это подтверждённый на форуме mailcow кейс. Нужно пересоздать контейнеры: docker compose up -d, а при сомнениях — docker compose down и затем docker compose up -d.

Что будет, если выделить Flatcurve слишком мало памяти?

Судя по документации, Dovecot не падает жёстко при нехватке памяти конкретному индексирующему процессу — он продолжает работать, но заметно медленнее. Это не повод экономить бездумно, но говорит о том, что ошибка в меньшую сторону безопаснее ошибки в большую: лучше начать с низких значений и поднимать их постепенно.

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

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

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

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

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

Источники

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