АйТи Фреш
Главная / Статьи / Серверы и хостинг
Серверы и хостинг

Письма есть на диске, а Dovecot их не показывает: когда помогает force-resync

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~23 мин чтения
Письма есть на диске, а Dovecot их не показывает: когда помогает force-resync
Иллюстрация к статье «Письма есть на диске, а Dovecot их не показывает: когда помогает force-resync».

Это статья для сисадмина, которому в понедельник утром говорят: «у Ивановой пропала половина папки, доставайте из бэкапа». Покажу, как за пять минут проверить, что письма на самом деле никуда не делись, какие служебные файлы Dovecot ломаются и в каком порядке их чинить, что именно делает doveadm force-resync и чего он не делает, и почему запуск с ключом -A превращает проблему одного ящика в проблему всего сервера. С командами, счётчиками и разбором ремонта на почтовом сервере небольшого юридического бюро.

Сначала посчитайте файлы, потом паникуйте

Звонок всегда звучит одинаково: «пропали письма, срочно из бэкапа». Первым делом я иду не в бэкап, а на сервер, и считаю файлы в каталоге папки. Чаще всего письма лежат на диске до последнего байта, просто Dovecot их не видит. IMAP-клиенту он отдаёт то, что записано в его индексе, а не то, что лежит в файловой системе. Индекс побился, и ящик «похудел», хотя данные при этом целы.

Проверка занимает минуту. Для maildir считаем файлы в cur и new и сравниваем с тем, что показывает сам Dovecot:

# сколько писем реально лежит на диске
find /var/vmail/firm.example/i.ivanova/Maildir/.Cases/{cur,new} -type f | wc -l

# сколько писем видит Dovecot
doveadm mailbox status -u i.ivanova@firm.example messages Cases

# то же самое сразу по всем папкам ящика, с суммой
doveadm mailbox status -u i.ivanova@firm.example -t messages '*'

Если файлов заметно больше, чем messages у Dovecot, это рассинхрон, а не удаление. Если числа совпадают, а пользователь всё равно не видит писем, проблема на стороне клиента: кэш OST у Outlook, свёрнутое представление, настройка «хранить письма за последние 12 месяцев», отписанная папка. И только если пусто и на диске, идём читать логи и доставать копию.

Цена ошибки здесь сильно отличается. Восстановление из бэкапа означает часы работы, чужие UID, возможно новую UIDVALIDITY и перекачку ящика у каждого, кто держит его в Outlook или на телефоне. Ремонт индекса занимает одну команду и несколько минут. Поэтому порядок жёсткий: посчитать, поставить диагноз, потом чинить. Типичный сценарий, который мне приходилось разгребать после других подрядчиков: поверх живого ящика разворачивают вчерашнюю копию, чтобы «вернуть письма», и вместо десятка пропавших сообщений пользователь получает дубли всей папки и долгую пересинхронизацию на всех устройствах.

Не разворачивайте бэкап поверх живого ящика, пока не сравнили счётчики. Восстановление «на всякий случай» стоит дороже самой аварии: дубли, смена UIDVALIDITY и перекачка гигабайтов у всех клиентов сразу.
Памятка: Сначала посчитайте файлы, потом паникуйте — схема
Памятка: Сначала посчитайте файлы, потом паникуйте. Открыть схему в полном размере

Что именно ломается: карта служебных файлов

Чтобы понимать, что чинит force-resync, надо держать в голове, из чего состоит ящик. Рядом с письмами Dovecot хранит несколько служебных файлов, и они очень разные по критичности. dovecot.index — основной бинарный индекс: соответствие последовательных номеров, UID и флагов. dovecot.index.log — журнал транзакций, куда пишутся изменения флагов и добавления, именно он чаще всего обрывается при внезапном ребуте. dovecot.index.cache — кэш заголовков и метаданных, чистый ускоритель. dovecot-uidlist в maildir — карта «UID к имени файла» плюс заголовок с UIDVALIDITY, next UID и GUID ящика. dovecot-keywords — таблица соответствия букв a–z именам IMAP-меток. dovecot.list.index — список папок пользователя.

Иерархия важности такая. Файл кэша — расходник: его можно удалять без последствий, Dovecot перестроит его при первом обращении. С версии 2.3.11 есть и частный автоматический случай: если кэш разросся сверх лимита и в логе появляется «Corrupted index cache file … Cache file too large», Dovecot удаляет такой dovecot.index.cache сам. Индекс и журнал восстановимы из самих писем, и это как раз работа force-resync. А вот dovecot-uidlist расходником не является: если он потерян, Dovecot выдаст папке новую UIDVALIDITY, а по стандарту IMAP это сигнал клиенту выбросить всё закэшированное и качать заново. Для ящика на 30 ГБ и офиса на одном канале это очень болезненно. Потеря dovecot.list.index даёт узнаваемый симптом: у пользователя появляются папки вида recovered-lost-folder-<GUID>, то есть Dovecot нашёл папку по идентификатору, но не смог восстановить её имя.

Отдельная история — форматы sdbox и mdbox. Там письма лежат не отдельными файлами maildir, а в собственных контейнерах, и UID, флаги и привязка писем к папкам хранятся именно в индексных файлах. Отсюда правило: rm dovecot.index* относительно безопасен на maildir и разрушителен на mdbox. Тела писем в файлах m.* останутся, но метаданные ящика будут потеряны, и собирать его придётся заново. Для dbox-форматов force-resync не просто перестраивает индекс, а дополнительно проверяет файлы хранилища, и это штатный путь ремонта.

Совет из интернета «просто удали dovecot.index*» написан для maildir. На mdbox он уничтожает метаданные ящика: UID, флаги, раскладку писем по папкам. Перед любым rm выясните формат хранилища: в 2.4 это настройка mail_driver, в 2.3 — префикс в mail_location (maildir:, sdbox:, mdbox:).
Письма есть на диске, а Dovecot их не показывает: когда помогает force-resync — схема
Схема к статье. Открыть схему в полном размере

Что force-resync делает и чего не делает

Формулировка в мануале узкая: команда нужна для ситуаций, когда Dovecot не смог решить проблему с ящиком автоматически, и она пытается исправить все найденные проблемы; для sdbox и mdbox дополнительно проверяются файлы хранилища. Это не профилактика, а ремонт по факту поломки. Полный синтаксис: doveadm force-resync [-S socket_path] [-A|-F file|--no-userdb-lookup|-u user] mailbox.

# починить INBOX одного пользователя
doveadm force-resync -u bob INBOX

# по списку пользователей из файла (один логин в строке)
doveadm force-resync -F /root/broken-users.txt INBOX

# все ящики домена: маска допустима в -u, но не в имени папки
doveadm force-resync -u '*@firm.example' INBOX

Ключи стоит различать чётко. Ключ -u выбирает пользователя, и в нём допустимы подстановки * и ?, то есть -u '*@firm.example' означает все ящики домена (кавычки обязательны, иначе звёздочку раскроет shell). Ключ -A обрабатывает всех пользователей из userdb; в мануале отдельно предупреждают, что с системными пользователями из passwd-драйвера так делать не надо. Ключ -F берёт список из файла, --no-userdb-lookup пропускает обращение к userdb и берёт пользователя из переменной USER. А вот последний аргумент, имя папки, подстановок не поддерживает, звёздочку туда подставлять бессмысленно.

Деталь, о которой часто забывают: для mdbox чинятся все папки пользователя, даже если аргументом указан INBOX. То есть на mdbox doveadm force-resync -u bob INBOX — ремонт всего ящика, а не одной папки. На maildir и sdbox обрабатывается только указанная папка, и если сломалось в трёх папках, придётся повторить команду три раза или пройтись циклом по выводу doveadm mailbox list:

doveadm mailbox list -u i.ivanova@firm.example | while read -r mb; do
  doveadm force-resync -u i.ivanova@firm.example "$mb"
done

Чего force-resync не делает. Он не возвращает удалённые письма — если файл стёрт с диска, восстанавливать нечего. Он не чинит права доступа и владельца файлов. Он не лечит причину: если раздел уходит в read-only из-за сыплющегося диска или у вас кончилось место, индексы поломаются снова через день. И он не бесплатен по ресурсам — на большом ящике это полное перечитывание всех писем, то есть много IOPS и, для mdbox, реальная нагрузка на хранилище.

force-resync — не профилактика. Гонять его по расписанию «чтобы не ломалось» — верный способ каждую ночь перечитывать терабайт почты и получить деградацию вместо надёжности.

Мой порядок действий: рунбук на восемь шагов

Порядок у меня всегда одинаковый. Шаг первый — зафиксировать факты: счётчики с диска и из Dovecot, текущие UIDVALIDITY, uidnext и GUID. Их надо записать до ремонта, иначе потом нечем будет доказать, что ничего не потеряно и не сменилось. Шаг второй — снять копию служебных файлов, а не всего ящика: они весят килобайты, копируются мгновенно и позволяют откатиться, если станет хуже.

M=/var/vmail/firm.example/i.ivanova/Maildir
doveadm mailbox status -u i.ivanova@firm.example \
  messages uidvalidity uidnext guid Cases | tee /root/before.txt

mkdir -p /root/idx-backup
tar czf /root/idx-backup/ivanova-$(date +%F-%H%M).tgz \
  $M/dovecot.list.index* $M/.Cases/dovecot-uidlist \
  $M/.Cases/dovecot.index* $M/.Cases/dovecot-keywords

Шаг третий — выгнать клиентов из ящика, иначе ремонт пойдёт наперегонки с живыми IMAP-сессиями, которые продолжают писать в тот же индекс. У doveadm who и doveadm kick пользователь указывается позиционным аргументом, без -u. Шаг четвёртый — собственно force-resync по одному ящику и одной папке. Шаг пятый — сверить счётчики с тем, что записали в before.txt.

doveadm who i.ivanova@firm.example
doveadm kick i.ivanova@firm.example

time doveadm -v force-resync -u i.ivanova@firm.example Cases

doveadm mailbox status -u i.ivanova@firm.example \
  messages uidvalidity uidnext guid Cases | diff /root/before.txt -

Шаг шестой — прогреть индекс, чтобы пользователь не ждал на первом открытии папки: doveadm index -u i.ivanova@firm.example -q Cases. Ключ -q ставит задачу в очередь процессу indexer, а не выполняет её прямо в doveadm, и на живом сервере это предпочтительный вариант. Шаг седьмой — попросить пользователя открыть почту и подтвердить, что всё на месте, прежде чем закрывать заявку. Шаг восьмой, который обычно пропускают, — разобраться, почему сломалось. Внезапный ребут, заполненный раздел, перемонтирование в read-only после ошибки I/O, две копии Dovecot на одном хранилище, ящик на NFS без корректной блокировки: причина почти всегда находится в dmesg или в логе за час до первой строчки Corrupted.

Если UIDVALIDITY после ремонта всё-таки изменилась (diff с before.txt это покажет), предупредите пользователей заранее, до того как они позвонят сами. Их клиенты начнут перекачивать папку целиком. Даже в офисе на 17 человек с Outlook и ящиками по 10–30 ГБ несколько одновременных перекачек легко забивают канал на полдня. Такой ремонт лучше планировать на вечер, чем объяснять последствия утром.

Копируйте dovecot-uidlist перед любым ремонтом. Это единственный файл, потеря которого гарантированно ударит по всем клиентам сразу — он весит килобайты, а спасает часы.
Порядок действий: Мой порядок действий: рунбук на восемь шагов — схема
Порядок действий: Мой порядок действий: рунбук на восемь шагов. Открыть схему в полном размере

Разбор из практики: юридическое бюро «Статья и Суд», 17 рабочих мест

Юридическое бюро, 17 рабочих мест, собственный почтовый сервер на виртуальной машине в офисной серверной: Debian, Postfix и Dovecot ветки 2.3, maildir, 24 ящика с учётом общих адресов вроде канцелярии и приёмной, около 320 ГБ почты. Утром заявка: у партнёра бюро в папке Cases, куда годами складывалась переписка по делам, вместо примерно 18 тысяч писем осталось меньше трёх, а в Outlook папка «мигает» — часть писем то появляется, то пропадает. Для юристов это не мелочь: в папке переписка с судами и доверителями, и партнёр уже требовал «поднять всё из бэкапа за вчера».

Считаем. find .../.Cases/{cur,new} -type f | wc -l даёт 18 614 файлов, doveadm mailbox status ... messages Cases показывает 2 907. Разница больше чем в шесть раз, значит письма на месте. В логе за ночь до этого пачка строк «Corrupted transaction log file» и «fscking index file», а в dmesg ошибки I/O виртуального диска, после которых ext4 с опцией errors=remount-ro перемонтировал раздел только на чтение. Сам в режим записи он не вернулся: утром сервер перезагружали, не разобравшись в причине. Картина сходится: журнал транзакций оборвался посреди записи, Dovecot дальше по нему прочитать не смог и показывал то, что успел. Файл dovecot-uidlist оказался цел, а значит, UID писем и UIDVALIDITY можно было сохранить.

Ремонт уложился примерно в десять минут вместе с проверками. Сохранили служебные файлы, выгнали две активные сессии (Outlook на рабочем месте и телефон) через doveadm kick, запустили force-resync по одной папке. На ящике около 23 ГБ команда отработала за несколько минут. После неё messages стало ровно 18 614, до единицы как файлов на диске. UIDVALIDITY, uidnext и GUID совпали с записанными до ремонта: все письма получили свои прежние UID из uidlist, поэтому Outlook ничего не перекачивал, а просто показал папку целиком. Затем поставили в очередь doveadm index, чтобы первое открытие не тормозило.

Поучительнее оказался второй эпизод. Через несколько дней похожая картина возникла в ящике помощника юриста, и приходящий специалист, не дождавшись нас, удалил из папки все служебные файлы разом, включая dovecot-uidlist. Письма вернулись, но UIDVALIDITY сменилась, и Outlook начал перекачивать папку объёмом около 9 ГБ, а заодно телефон и планшет того же сотрудника. Канал офиса был занят до обеда, у остальных медленно открывались системы правовых баз. Ни одного письма не пропало, но для бюро это ощущалось серьёзнее исходной аварии. После этого в регламент записали: из каталога папки Dovecot вручную можно удалять только dovecot.index.cache, всё остальное — через force-resync и только после копии.

Первопричину закрыли отдельно. Виртуальный диск лежал на сбоящем накопителе хоста: его заменили, а в мониторинг добавили триггер на строки Corrupted и Broken file в журнале Dovecot и на перемонтирование файловой системы в read-only. Показательно, что внешний мониторинг «по старинке» ни одного из двух инцидентов не заметил: порты слушались, письма принимались и доставлялись. Ломался один ящик, и увидеть это можно было только по логу.

Мониторинг доступности почты такой сбой не ловит: сервис жив, порты слушаются, письма ходят. Ставьте триггеры на «Corrupted», «Broken file», «fscking index» в журнале Dovecot и на remount-ro в dmesg, иначе про битые индексы вы узнаете от пользователя.

Пять способов сделать хуже

Первое — ключ -A в рабочее время. Соблазн понятен: «непонятно, у кого ещё сломалось, прогоню по всем». На сервере с сотней ящиков и сотнями гигабайт это полное перечитывание всей почты. Письма никуда не денутся, но дисковая подсистема встанет колом, IMAP начнёт отваливаться по таймауту, и вместо одной заявки получится массовый простой. Если ремонт нужен многим, соберите список в файл и запускайте через -F порциями, вечером.

Второе — чинить при живых сессиях. Пользователь в этот момент открывает папку, клиент пишет флаги, а вы перестраиваете индекс, и после ремонта картина снова разъезжается. Если ящик критичный, одного kick мало: телефон переподключится через полминуты. На время работ я закрываю вход через отдельный passdb с флагом deny (синтаксис Dovecot 2.3; в 2.4 блоки passdb оформляются иначе, сверяйтесь с документацией своей версии):

# /etc/dovecot/conf.d/auth-deny.conf.ext, подключить до основного passdb
passdb {
  driver = passwd-file
  deny = yes
  args = /etc/dovecot/deny-users
}

В файл /etc/dovecot/deny-users кладём строку i.ivanova@firm.example, после ремонта убираем. Кэш аутентификации, если он включён, сбрасываем командой doveadm auth cache flush.

Третье — удалять служебные файлы «пакетом». На maildir это переживаемо, если не задеть dovecot-uidlist. На mdbox это потеря метаданных: UID, флагов и раскладки писем по папкам. Четвёртое — копировать и править файлы под root. Один забытый chown после ручного копирования, и Dovecot не сможет писать в каталог, а вы будете искать причину в конфигах. Файлы должны принадлежать тому же пользователю, что и остальная почта, обычно vmail.

Пятое — лечить симптом, не глядя на железо. Если индексы бьются регулярно, force-resync превращается в ежедневный ритуал, а реальная причина сидит ниже: сбоящий диск, кончающееся место, два инстанса Dovecot на одном хранилище, maildir на NFS без нормальной блокировки, отсутствие корректного завершения при выключении. Отдельная классика — квоты: заполненный до предела раздел ломает запись индекса именно в момент, когда почта активнее всего идёт. Держите на почтовом разделе запас хотя бы в 15–20 процентов и алерт на 85 процентов заполнения.

Прежде чем набирать -A, выпишите затронутых пользователей в файл. Ключ -F с небольшой порцией ящиков решает ту же задачу и не роняет сервер.
Цифры и версии: Пять способов сделать хуже — схема
Цифры и версии: Пять способов сделать хуже. Открыть схему в полном размере

Профилактика: что реально снижает частоту таких заявок

На первом месте у меня — питание и корректное выключение. Большинство битых индексов, которые я разбирал, родом из внезапного ребута: пропало электричество, ушёл в перезагрузку гипервизор, кто-то дёрнул виртуалку. ИБП с корректным shutdown-агентом и настроенный таймаут остановки сервисов решают больше, чем любые тюнинги Dovecot. На втором месте — свободное место и здоровье дисков: алерт на 85 процентов заполнения почтового раздела, SMART-мониторинг, триггер на переход ФС в read-only.

На третьем — мониторинг именно логов Dovecot, а не доступности порта 993. Ключевые маркеры: Corrupted index cache file, Broken file, fscking index file, recovered-lost-folder. Последний, кстати, самый показательный: он означает, что побился уже не индекс папки, а список папок, и пользователь увидит каталоги с именами вида recovered-lost-folder-<GUID>. Разбираются они через doveadm move содержимого в правильную папку и последующее переименование — но лучше до этого не доводить.

Четвёртое — версия. Ветка 2.3 официально получает только критичные исправления безопасности, обычные баги в ней разработчики уже не разбирают. Актуальная ветка — 2.4.x, на момент написания последний релиз 2.4.5 от 28 августа 2026 года. Переезд на 2.4 при этом не бесплатный: переработан формат конфигурации, привычный mail_location разбит на mail_driver, mail_path и mailbox_list_layout, и старая настройка не подхватывается. Планируйте это как отдельные работы с тестовым стендом, а не как apt upgrade между делом.

Последнее по срочности, но не по важности — бэкап. Индексы бэкапить смысла нет, они перестраиваются. Бэкапить надо письма, и так, чтобы восстановление одного ящика или одной папки не требовало разворачивать весь сервер. Я держу два уровня: снимок хранилища и отдельно выгрузку через doveadm backup на другую машину. Второй вариант дороже по месту, но позволяет вернуть один ящик без остановки почты. Часть коллег считает doveadm backup избыточным при наличии снапшотов; по моему опыту, восстановление одной папки из снапшота руками всегда превращается в возню с правами, UID и последующим force-resync.

Апгрейд с 2.3 на 2.4 нельзя делать «заодно»: mail_location больше не работает и молча игнорируется, вместо него mail_driver + mail_path + mailbox_list_layout. Проверяйте конфиг на тестовом стенде до боевого сервера.

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

Может ли force-resync удалить письма?

Сама по себе — нет: команда перестраивает индексы по тому, что лежит в хранилище, и для sdbox/mdbox дополнительно проверяет файлы хранилища. Проблемы начинаются от ручного удаления служебных файлов до запуска. На maildir опасна потеря dovecot-uidlist (новая UIDVALIDITY и перекачка у клиентов), на mdbox — удаление индексных файлов вообще: там в них UID, флаги и раскладка писем по папкам. Поэтому перед ремонтом делайте копию служебных файлов, они весят килобайты.

Нужно ли останавливать Dovecot перед force-resync?

Останавливать сервис целиком не нужно: вы положите почту всей компании ради одного ящика. Достаточно выгнать сессии конкретного пользователя: `doveadm who user@domain`, затем `doveadm kick user@domain` (у обеих команд пользователь указывается позиционно, ключа -u нет). Если клиенты переподключаются слишком быстро, временно закройте вход через passdb с deny = yes. При ремонте на фоне активной IMAP-сессии клиент продолжит писать в тот же индекс, и картина может разъехаться снова.

Что означает ключ -A и почему его не советуют?

-A запускает команду для всех пользователей из userdb. На сервере с сотнями ящиков это полное перечитывание всей почты и практически гарантированный отказ по дисковой подсистеме в рабочее время. Плюс в мануале отдельно предупреждают, что с системными пользователями из passwd-драйвера -A использовать не стоит. Если чинить надо многим — собирайте список в файл и запускайте через -F порциями, вне рабочих часов.

Почему после ремонта Outlook перекачал весь ящик заново?

Сменилась UIDVALIDITY — значение, которое клиент использует, чтобы понять, можно ли доверять своему кэшу. Обычно это следствие потери файла dovecot-uidlist в maildir. По стандарту IMAP смена UIDVALIDITY означает «забудь всё и скачай заново», поэтому папка перекачивается целиком, а при большом ящике это десятки гигабайт трафика. Фиксируйте uidvalidity до и после ремонта: если она не изменилась, перекачки не будет.

Сколько времени занимает force-resync?

Зависит от объёма, числа писем и дисков. В описанном кейсе папка на 18 тысяч писем в ящике около 23 ГБ на maildir чинилась несколько минут. Для mdbox дольше, потому что проверяются ещё и файлы хранилища, и команда чинит все папки пользователя, даже если аргументом указан INBOX. Крупные ящики и массовый ремонт через -F планируйте на нерабочее время.

Откуда взялись папки вида recovered-lost-folder-123456?

Повредился или потерялся dovecot.list.index — список папок пользователя. Dovecot нашёл каталог по GUID, но не смог восстановить его имя и показал такое техническое название. Лечится переносом содержимого в правильную папку через doveadm move и последующим переименованием. Признак того, что ломается не одна папка, а инфраструктура ниже: место на диске, файловая система, метаданные.

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

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

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

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

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

Источники

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