Включаю Flatcurve в mailcow и не кладу сервер: расчёт CPU и памяти под индексацию
Документация 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-макеты).
- Flatcurve менее ресурсоёмкий, чем Solr, но не бесплатный по CPU и памяти
- после обновления с версии до 2025-01 FTS отключается автоматически
- решение «не трогать» = жить без полнотекстового поиска, не всегда оправданно
- решение — считать лимиты под конкретное железо, а не включать вслепую
Почему дефолты такие скромные — и что это значит для вас
Официальная документация формулирует это прямо: 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_PROCS` по умолчанию — 1 (один индексирующий процесс одновременно)
- `FTS_HEAP` по умолчанию — 128 МБ на процесс
- каждый индексирующий процесс однопоточный, занимает целое ядро CPU
- дефолты рассчитаны на самый слабый сценарий, а не оптимальны для среднего сервера
Как включить правильно: одна строка в конфиге и одна ловушка с перезапуском
Формально включение — это одна правка: в 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.
- `SKIP_FTS=n` в mailcow.conf — обязательное условие
- `docker compose up -d` из документации — рабочий способ в штатном сценарии
- reboot хоста и `docker compose restart` не применяют mailcow.conf — нужен `up -d` или `down` + `up -d`
- перезагрузка хоста ≠ пересоздание стека контейнеров для конфигурации FTS-плагина
Расчёт FTS_PROCS под число ядер CPU
Правило из документации звучит просто — закладывайте под индексацию примерно половину потоков CPU системы, а при нечётном числе потоков округляйте вниз, чтобы гарантированно оставить достаточно ресурсов основным сервисам. Отдельно и однозначно: системы с одним или двумя ядрами документация рекомендует держать с полностью отключённым FTS — не с минимальным FTS_PROCS=1, а именно выключенным, потому что на таком железе даже один занятый под индексацию поток CPU — это половина или вся вычислительная мощность сервера.
На практике это выглядит как простая таблица, которую я использую как отправную точку в разговоре с клиентом перед первым включением:
| Ядра/потоки CPU | FTS_PROCS | Комментарий |
|---|---|---|
| 1-2 | FTS не включать | документация: отключить FTS полностью |
| 3 | 1 | половина, округлённая вниз |
| 4 (наш случай) | 2 | ровно половина |
| 6 | 3 | половина |
| 8 | 4 | половина |
У «Синема Гранд» сервер с 4 vCPU, и по этому правилу отправная точка — FTS_PROCS=2. Я сознательно не стал брать максимум, разрешённый формулой, для первого запуска: начал с FTS_PROCS=1 на время первичной полной индексации архива (несколько месяцев переписки с прокатчиками), посмотрел на docker stats во время процесса, и только после спокойной первой индексации поднял значение до FTS_PROCS=2 для последующей штатной фоновой работы.
- правило документации: примерно половина потоков CPU, нечётное число — округлять вниз
- 1-2 ядра — FTS рекомендуется не включать вообще
- 4 vCPU (случай «Синема Гранд») — целевое значение FTS_PROCS=2
- для первичной полной индексации разумно стартовать консервативнее целевого значения
Расчёт 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: там тоже важно не путать мягкий лимит с жёстким и не резать оба сразу вслепую.
- идеал по документации — 512 МБ на процесс индексации при достаточном запасе RAM
- меньше 8 ГБ RAM — держаться 128-256 МБ, сокращая число процессов
- нехватка памяти замедляет Dovecot, но не роняет его — риск деградации, а не аварии
- CPU и RAM — независимые лимиты, увеличивать нужно тот, где реально есть запас
Первая индексация: что смотреть и когда поднимать лимиты
Запуск первичной индексации я всегда делаю вручную и под наблюдением, а не полагаюсь на автоматическую фоновую индексацию по мере накопления новых писем — для архива в несколько месяцев переписки это заняло бы недели. Команда для одного пользователя за раз, чтобы видеть прогресс и иметь возможность остановиться:
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.
- индексировать вручную по одному пользователю, не полагаться только на автоиндексацию для архива
- `docker stats` во время первого прогона — смотреть не только на CPU, но и на память MySQL/Dovecot вместе
- первый прогон — вне рабочего времени, консервативные лимиты
- лимиты поднимать постепенно, после недели наблюдения за стабильной фоновой работой
Частые вопросы
Можно ли включить 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 не падает жёстко при нехватке памяти конкретному индексирующему процессу — он продолжает работать, но заметно медленнее. Это не повод экономить бездумно, но говорит о том, что ошибка в меньшую сторону безопаснее ошибки в большую: лучше начать с низких значений и поднимать их постепенно.
Источники
- mailcow docs: Full-Text Search — FTS_PROCS и FTS_HEAP — Дефолты FTS_PROCS=1, FTS_HEAP=128 МБ; рекомендация «половина потоков CPU», однопоточность индексатора, отключение FTS на 1-2 ядрах, вилка памяти для систем менее 8 ГБ RAM и поведение Dovecot при нехватке памяти. https://docs.mailcow.email/manual-guides/Dovecot/u_e-dovecot-fts/
- mailcow blog: Janmooary 2025 Update — Официальное объявление о замене Solr на Flatcurve, автоматическое отключение SKIP_FTS после обновления, предупреждение о возможных пиках CPU при индексации. https://mailcow.email/posts/2025/release-2025-01/
- mailcow community: Flatcurve indexing does not seem to work — Подтверждённый кейс: полная перезагрузка хоста не пересоздаёт конфигурацию FTS-плагина, требуется docker compose down + up -d. https://community.mailcow.email/d/4479-flatcurve-indexing-does-not-seem-to-work



