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

Снесли служебные файлы Dovecot в Maildir: почему клиенты выкачивают всю почту заново и куда делись метки

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~26 мин чтения
Снесли служебные файлы Dovecot в Maildir: почему клиенты выкачивают всю почту заново и куда делись метки
Иллюстрация к статье «Снесли служебные файлы Dovecot в Maildir: почему клиенты выкачивают всю почту заново и куда делись метки».

Это статья для сисадмина, который ночью освобождал место на почтовом сервере, снёс в Maildir всё, что начинается на «dovecot», и утром получил load average 14, забитый канал и полтора десятка звонков «пропали мои метки». Покажу, какие файлы в Maildir — кэш, а какие данные; почему смена UID заставляет Outlook и Thunderbird выкачать ящик с нуля; что из потерянного реально вернуть из бэкапа, а что уже нет; и как разложить каталоги так, чтобы следующая «чистка индексов» физически не могла добраться до управляющих файлов. С командами, разбором формата dovecot-uidlist и реальным ремонтом на 24 ящика в стоматологической клинике.

«Почистили кэш Dovecot» — и утром сервер в полке

Звонок в 9:20: «почта тормозит, Outlook второй час что-то качает, и пропали все мои цветные категории». Захожу на сервер — load average 14 на четырёх ядрах, iowait 64 %, исходящего трафика за утро 17 ГБ при обычных 600 МБ в сутки. Смотрю историю команд предыдущей смены и нахожу красивое: find /var/vmail -name 'dovecot*' -delete. На разделе кончилось место, человек решил «почистить индексы, они же всё равно пересоберутся».

Пересоберутся. Но не все. В каталоге Maildir рядом лежат файлы двух принципиально разных классов, и по имени они почти не различаются — и те и другие начинаются на dovecot. Одни — кэш, их можно сносить хоть каждый день, ничего не случится. Другие — данные, и их удаление эквивалентно потере части почтового ящика. Документация Dovecot говорит об этом прямым текстом, без иносказаний: индексные файлы можно удалять и пересобирать без побочных эффектов, но если удалить control files — «сообщения получат новые UID и, возможно, потеряются названия ключевых слов».

Вот эта одна фраза из мануала и стоит вам ночи ремонта плюс дня объяснений с бухгалтерией. Дальше разберу по шагам: какие именно файлы трогать нельзя, почему смена UID заставляет клиента выкачать ящик с нуля, что из потерянного реально вернуть, и как разложить каталоги так, чтобы грабли больше не собирались.

Сразу успокою по одному пункту, потому что паника обычно начинается не с того. Сами письма при такой «чистке» не пропадают. Ни одного байта. Файлы в cur/ и new/ лежат как лежали, find по маске dovecot* их не трогает. Вы потеряли не почту, вы потеряли метаданные о почте — и это лечится дешевле, чем восстановление 240 ГБ из бэкапа. Но потеряли по-настоящему, и делать вид, что «клиент сам разберётся», не выйдет.

Правило, которое я вбиваю каждому инженеру на входе: в Maildir рукой удалять можно только то, что называется `dovecot.index*`. Всё, что с префиксом `dovecot-` через дефис, — это данные. Точка против дефиса — вся разница между «пересоберётся за минуту» и «полдня никто не может работать с почтой».
Памятка: «Почистили кэш Dovecot» — и утром сервер в полке — схема
Памятка: «Почистили кэш Dovecot» — и утром сервер в полке. Открыть схему в полном размере

Что на самом деле лежит в каталоге Maildir

Разберём по файлам. Возьмём папку одного ящика на боевом сервере — это стандартная раскладка Maildir++ с виртуальными пользователями:

ls -la /var/vmail/dental-dom.example/i.ivanova/Maildir/.Laboratoriya/

В выводе вы увидите три каталога — cur, new, tmp — и горсть служебных файлов. cur и new содержат сами письма, по одному файлу на сообщение. Всё остальное — метаданные, и именно с ними надо разбираться внимательно.

dovecot-uidlist — самый важный файл. Он держит соответствие между IMAP UID сообщения и именем файла на диске. Формат текстовый, читается глазами. Первая строка — заголовок:

3 V1275660208 N25022 G3085f01b7f11094c501100008c4a11c1
25006 :1276528487.M364837P9451.kurkku,S=1355,W=1394:2,
25017 W2481 :1276533073.M242911P3632.kurkku:2,F

Первое число 3 — версия формата, которую Dovecot использует начиная с v1.1 (версия 1 совместима с Courier). Пример взят из официальной документации, имя хоста kurkku в именах файлов — оттуда же. V1275660208 — UIDVALIDITY папки. N25022 — UID, который будет выдан следующему добавленному сообщению. G3085f01b... — 128-битный глобальный идентификатор папки в hex. Дальше идут записи: UID, необязательные поля (например W2481 — размер сообщения в RFC822-представлении, с CRLF), двоеточие и последнее известное имя файла.

dovecot-keywords — второй управляющий файл, и про него забывают вообще все. В имени файла Maildir пользовательские метки кодируются одной буквой: :2,STa означает флаги Seen, Trashed и пользовательскую метку под буквой a. А что такое a — написано только здесь:

0 $Junk
1 $NonJunk
2 Договоры
3 Страховые

Позиция 0 соответствует букве a, позиция 1 — b и так далее. В имени файла помещается максимум 26 меток; всё, что сверх, живёт в индексах. Удалили dovecot-keywords — буквы в именах файлов остались, а расшифровки нет. Клиент видит либо пустоту, либо непонятный набор служебных ключевых слов вместо ваших «Договоров» и «Страховых».

И третья группа — индексы: dovecot.index (текущее состояние флагов и заголовков), dovecot.index.log (журнал транзакций), dovecot.index.cache (кэш разобранных заголовков и структуры MIME, чтобы не перечитывать письма при каждом поиске). Вот это — производные данные. Их можно удалить в любой момент; Dovecot пересоберёт их из имён файлов и из dovecot-uidlist. Именно потому, что индексы восстанавливаются из uidlist, а не наоборот, uidlist и является тем самым единственным источником правды.

Проверить, что вы вообще потеряли, можно за секунду: `head -1 /var/vmail/domain/user/Maildir/.Folder/dovecot-uidlist`. Если файла нет, а письма в `cur/` есть — вы попали ровно в тот сценарий, о котором эта статья.
Снесли служебные файлы Dovecot в Maildir: почему клиенты выкачивают всю почту заново и куда делись метки — схема
Схема к статье. Открыть схему в полном размере

Почему из-за нового UID клиент выкачивает ящик заново

Механика простая, но её надо проговорить, иначе непонятно, откуда берутся десятки гигабайт трафика на ровном месте. IMAP не идентифицирует письмо по содержимому. Он идентифицирует его парой «UIDVALIDITY папки + UID сообщения». Клиент кэширует эту пару локально: в OST у Outlook, в .msf-индексе у Thunderbird, в базе у мобильного клиента. При подключении он спрашивает у сервера UIDVALIDITY. Совпал — можно догружать только новое, по дельте. Не совпал — весь локальный кэш этой папки объявляется недействительным.

RFC 9051 (IMAP4rev2, август 2021) в разделе 2.3.1.1 формулирует это со стороны сервера: если уникальные идентификаторы не сохранились между сессиями, значение UIDVALIDITY обязано стать больше предыдущего. Явного «клиент ДОЛЖЕН всё перекачать» в тексте нет, но следствие однозначное: изменившийся UIDVALIDITY означает, что старые UID больше не указывают на те же самые письма, и любой offline-клиент, который на них опирался, обязан ресинхронизироваться с нуля. На практике все нормальные клиенты именно это и делают.

Теперь что происходит, когда вы удалили dovecot-uidlist. Dovecot открывает папку, файла нет — он создаёт его заново. Присваивает UID всем найденным письмам заново, обычно начиная с единицы, в порядке сортировки имён файлов. И генерирует новый UIDVALIDITY, потому что старый он взять неоткуда. Всё. С точки зрения любого клиента это уже другая папка. Не «изменившаяся» — другая. Тридцать тысяч писем в ящике главного врача приезжают к нему по IMAP повторно, все до одного.

Дальше начинается арифметика, которая и роняет сервер. Один ящик на 20 ГБ — это 20 ГБ дискового чтения и столько же исходящего трафика. Два десятка ящиков, которые проснулись одновременно в 8:50 на сервере, собранном под 22 рабочих места, — это уже не про канал, это про то, что дисковая подсистема встала колом, а imap-процессов стало столько, что вы упёрлись в лимит процессов сервиса (в 2.3 по умолчанию default_process_limit = 100) и живые пользователи получают отказ в подключении. Документация Dovecot прямо предупреждает: если сделать это для всех пользователей, получите «huge disk I/O bursts», и это не преувеличение, а описание вашего утра.

Отдельная неприятность — Outlook. Он в этой ситуации перестраивает OST-файл целиком. У врача с перепиской и снимками за восемь лет это 10–40 ГБ и несколько часов, в течение которых он не работает вообще, а вы объясняете руководителю, почему «ничего не удаляли, но всё качается заново». Веб-клиенты вроде Roundcube переживают проще — у них нет offline-кэша, — но и у них открытое окно со списком писем после смены UIDVALIDITY приходится перезагружать, а поиск и сортировка первые минуты тормозят, пока сервер заново строит кэш заголовков.

Хорошая новость, о которой редко говорят: базовые флаги IMAP — прочитано, отвечено, помечено, удалено — хранятся не в служебных файлах, а прямо в имени файла письма. Их вы не теряете. Теряете только пользовательские метки и связь UID↔письмо.

Разбор: клиника «Дентал-Дом», 24 ящика и одна команда find

Стенд, о котором речь. Стоматологическая клиника «Дентал-Дом» на 22 рабочих места: регистратура, врачи, зуботехническая лаборатория, бухгалтерия. Свой почтовый сервер на виртуальной машине: Debian 12, Dovecot 2.3.19.1 из штатного репозитория, Postfix спереди, 4 vCPU, хранение — Maildir на отдельном LVM-томе /var/vmail, 240 ГБ занято из 260. Двадцать четыре ящика — 22 сотрудника плюс общие info@ и registratura@; самый крупный — 46 ГБ у главного врача (сканы договоров, снимки КТ во вложениях и переписка с лабораториями за девять лет, чистить отказывается принципиально). Бэкап — файловый, каждую ночь в 03:10, ретеншен 14 дней.

Что произошло. Мониторинг в 01:40 прислал алерт «свободно менее 8 % на /var/vmail». Дежурный решил освободить место и, отталкиваясь от статьи в интернете про «безопасное удаление индексов Dovecot», выполнил под root:

find /var/vmail -name 'dovecot*' -delete

Маска dovecot* захватила и dovecot.index*, и dovecot-uidlist, и dovecot-keywords. Освободилось 3,4 ГБ — задача формально выполнена, алерт погас, человек ушёл спать. Первые пользователи начали подключаться в 07:30.

Что мы увидели в 09:20, когда меня позвали. Load average 14 при 4 ядрах, iowait 64 %, iostat показывает очередь к диску под 60. Процессов imap — 96 при default_process_limit = 100, то есть регистратура уже ловила отказы в подключении. По iftop исходящий трафик держался на 60–90 Мбит/с при офисном канале в 100, суммарно за утро наружу ушло 17 ГБ. Характерной строки UIDVALIDITY changed в логе не было — и это тоже диагноз: такое сообщение появляется, когда uidlist расходится с уцелевшим dovecot.index, а здесь снесли и то и другое. Диагноз поставили иначе: doveadm mailbox status показывал у папок uidvalidity, равный unix-времени сегодняшнего утра, и uidnext ровно на единицу больше числа писем, а mtime у dovecot-uidlist был свежее 01:40.

Первое, что сделали, — остановили Dovecot. Это контринтуитивно, когда «почта не работает», но абсолютно необходимо: пока сервис жив, он для каждой открытой папки (в том числе при доставке через LMTP) дописывает новый dovecot-uidlist с новыми UID, и число пострадавших ящиков растёт каждую минуту. Дальше сняли слепок текущего состояния (мало ли), составили список пользователей, которые уже успели войти по IMAP после аварии, — их клиенты уже перекачали папки под новые UID, и второй откат устроил бы им повторную перекачку. Остальным из ночного бэкапа за 03:10 восстановили точечно только управляющие файлы:

systemctl stop dovecot

# кто уже логинился по IMAP после 01:40 — их каталоги не трогаем
journalctl -u dovecot --since '01:40' \
  | grep -o 'Login: user=<[^>]*>' \
  | sed 's#.*<\(.*\)@\(.*\)>#/\2/\1/#' | sort -u > /root/synced-users.txt

# из смонтированного снапшота бэкапа — ТОЛЬКО control files
rsync -a --exclude-from=/root/synced-users.txt \
  --include='*/' \
  --include='dovecot-uidlist' \
  --include='dovecot-keywords' \
  --include='subscriptions' \
  --exclude='*' \
  /mnt/backup/var/vmail/ /var/vmail/
chown -R vmail:vmail /var/vmail
systemctl start dovecot

Почему нельзя было просто откатить /var/vmail целиком из бэкапа: между 03:10 и 09:20 люди читали и удаляли почту, приходили новые письма. Полный откат вернул бы удалённое и потерял бы доставленное за шесть часов. Точечный rsync только по трём именам файлов оставляет письма как есть и возвращает ровно ту метаинформацию, которую снесли. Ключевой момент — порядок фильтров: исключения пользователей идут первыми, затем --include='*/', без которого rsync не зайдёт в подкаталоги и молча ничего не скопирует. Побочный эффект того же */: папки, которые пользователь удалил после 03:10, вернутся пустыми каталогами — у нас таких оказалось две, их убрали руками. Свежесозданные ночью dovecot.index* в восстановленных папках трогать не нужно: при первом открытии Dovecot увидит расхождение UIDVALIDITY между индексом и вернувшимся uidlist, запишет в лог то самое UIDVALIDITY changed и перестроит индекс.

Итог. 15 ящиков из 24 вернулись к старым UID полностью — эти люди даже не заметили ночного происшествия, кроме подтормаживания утром. 9 ящиков — регистратура, два администратора и врачи утренней смены — успели до нашего вмешательства подключиться и начать перекачку под новые UID; их мы сознательно исключили из отката, и они честно пересинхронизировались — в сумме ещё около 12 ГБ трафика, растянутых на полтора дня, потому что мы порезали полосу для IMAP на шейпере. У двух ящиков, где dovecot-keywords в бэкапе оказался старше последних изменений, потерялись две пользовательские метки — их пересоздали руками. Общее время активного ремонта — пять часов, полное затухание последствий — двое суток.

И честный вывод, который я всегда проговариваю клиенту: спасло нас не мастерство, а то, что бэкап делался файловым уровнем и захватывал служебные файлы. Если бы почта бэкапилась только через doveadm backup или выгрузкой писем, вернуть исходные UID было бы нечем — все 24 ящика ушли бы в полный пересинк.

Если вы читаете это прямо сейчас с лежащим сервером — первым делом `systemctl stop dovecot`, и только потом всё остальное. Каждая минута работающего сервиса добавляет к списку потерь ещё несколько ящиков, для которых бэкап уже бесполезен.
Цифры и версии: Разбор: клиника «Дентал-Дом», 24 ящика и одна команда find — схема
Цифры и версии: Разбор: клиника «Дентал-Дом», 24 ящика и одна команда find. Открыть схему в полном размере

Что реально можно вернуть, а что уже нет

Разложу по честности, без обещаний. dovecot-uidlist возвращается только из бэкапа и только до того, как Dovecot успел создать новый. Восстановить старые UID «вычислением» нельзя: соответствие UID↔файл нигде больше не дублируется. Если у ящика уже появился свежий uidlist и клиент к нему подключился — всё, вы в новой реальности, откат назад только добавит путаницы. Не пытайтесь героически «вернуть как было» на таких ящиках: смиритесь и дайте им пересинкаться.

dovecot-keywords иногда спасается сам. Названия меток дублируются в индексе — в dovecot.index, — и если снесли только keywords, а индекс уцелел, названия часто ещё живы. Проверяется одной командой:

doveadm fetch -u i.ivanova@dental-dom.example flags mailbox Laboratoriya all | sort -u | head -30

Если в выводе видны нормальные названия — не удаляйте индекс: пока он жив, Dovecot знает названия и при очередном изменении меток запишет dovecot-keywords заново. На всякий случай сохраните этот вывод в файл — он пригодится, если индекс всё же придётся пересобирать. Если видите пустоту или служебные $Junk/$NonJunk вместо ваших рабочих меток — названия ушли, восстанавливать вручную по списку от пользователя.

Буквы-то в именах файлов остались. То есть если в бэкапе есть старый dovecot-keywords, вы возвращаете его, и все метки на всех письмах оживают одномоментно — потому что привязка «письмо ↔ буква» никуда не девалась, потерялась только расшифровка буквы. Это единственный сценарий в нашей теме, где восстановление даёт почти стопроцентный результат — оговорка одна: если после аварии пользователи успели создать новые метки, Dovecot мог занять под них те же буквы, и такие метки придётся перепроверить. Поэтому dovecot-keywords из бэкапа тащим всегда, даже если по uidlist уже поздно.

subscriptions восстанавливается из бэкапа тривиально, а если бэкапа нет — придётся просить пользователей подписаться на папки заново. Мелочь, но звонков генерирует много, потому что человек видит «пропали папки» и думает, что пропала почта. Предупредите рассылкой заранее — сэкономите себе полдня телефонных разговоров.

И последнее по приоритету, но важное по нервам: сообщите людям, что происходит, до того как они спросят. Формулировка, которая работает: «письма все на месте, ни одно не потеряно; почтовые программы сегодня перекачивают архив заново, это займёт до конца дня, работать можно через веб-почту». Это чистая правда и она снимает 80 % паники. Врать про «плановые работы» не надо — во-первых, узнают, во-вторых, к вам потом не придут вовремя с настоящей проблемой.

Не восстанавливайте каталог Maildir целиком из ночного бэкапа ради служебных файлов. Вы вернёте удалённые письма и потеряете доставленные с момента бэкапа — то есть превратите неприятность в настоящий инцидент с потерей данных.

Как чистить Dovecot, когда действительно кончилось место

Начну с главного: если вы удаляете файлы Dovecot, чтобы освободить место, вы почти наверняка чистите не то. Индексы редко занимают больше нескольких процентов от объёма почты. В том же «Дентал-Доме» удаление всех индексов дало 3,4 ГБ при 240 ГБ хранилища — полтора процента и двое суток последствий. Место занимают письма и вложения, а не метаданные.

Если индексы всё-таки распухли аномально — а это бывает, когда dovecot.index.cache разрастается на огромных папках, — чистить их надо инструментом Dovecot, а не find. Для одного ящика:

# пересобрать индексы одной папки, control files не трогаются
doveadm force-resync -u i.ivanova@dental-dom.example Laboratoriya

# все папки одного ящика
doveadm force-resync -u i.ivanova@dental-dom.example '*'

# посмотреть, что вообще с папкой: UID, UIDVALIDITY, GUID, размер
doveadm mailbox status -u i.ivanova@dental-dom.example \
  messages uidnext uidvalidity guid vsize Laboratoriya

По man-странице force-resync «пытается исправить все проблемы» ящика, которые Dovecot не смог решить автоматически. Для Maildir это пересканирование cur/ и new/ и перестройка индекса поверх существующего dovecot-uidlist — сам uidlist команда не удаляет, и UID сохраняются. Это ровно та операция, которую хотел сделать наш дежурный.

Ключ -A (по всем пользователям) — вещь, которой я пользуюсь очень аккуратно и никогда в рабочее время. doveadm force-resync -A '*' даже на сервере с 24 ящиками и 240 ГБ — это час-другой плотной дисковой нагрузки. И учтите оговорку из man: с userdb на базе passwd ключ -A захватывает и системных пользователей ниже first_valid_uid; в такой конфигурации надёжнее -F со списком ящиков. Запускать под nice/ionice, ночью, и лучше циклом по пользователям с паузами, чтобы не положить дисковую подсистему одним залпом.

Реальные способы освободить место на почтовом сервере, в порядке отдачи: вычистить корзины и спам старше N дней, поджать или вынести архивные ящики уволенных, включить квоты (их обычно нет — и зря), проверить, не растёт ли /var/log или снапшоты LVM. Массовое удаление старой почты:

# сколько занимает и сколько писем — сначала посчитать
doveadm quota get -A
doveadm search -A mailbox Trash savedbefore 30d | wc -l

# и только потом удалять
doveadm expunge -A mailbox Trash savedbefore 30d
doveadm expunge -A mailbox Junk savedbefore 30d

Это честные гигабайты, и никаких UID при этом не меняется. Две оговорки: doveadm quota get работает только при включённом плагине quota, а doveadm expunge по документации требует и имя папки, и ограничение по сообщениям — поэтому без savedbefore или другого условия он просто откажется выполняться, и это хорошо.

И про порядок действий, когда алерт по месту прилетел ночью. Не надо ничего удалять в 01:40 в полусне. Расширьте том, если есть чем; если нечем — почистите Trash и Junk по команде выше, это безопасно и почти всегда даёт достаточно. Всё остальное — утром, на свежую голову, с бэкапом под рукой. Ни один почтовый сервер не умирает от 92 % занятого места за одну ночь, а вот от неаккуратного find — умирает за одну минуту.

Ни одна команда `doveadm` не удаляет `dovecot-uidlist`. Если вы работаете штатным инструментом, а не `find` и `rm`, описанная в статье авария технически невозможна. Это самый дешёвый способ её предотвратить.
Цифры и версии: Как чистить Dovecot, когда действительно кончилось место — схема
Цифры и версии: Как чистить Dovecot, когда действительно кончилось место. Открыть схему в полном размере

Профилактика: развести control и index по разным каталогам

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

# Dovecot 2.3
mail_location = maildir:~/Maildir:INDEX=/var/indexes/%u:CONTROL=/var/control/%u

В ветке 2.4 настройка mail_location разобрана на отдельные параметры — это одно из заметных изменений при переходе, и его надо учитывать при апгрейде:

# Dovecot 2.4
mail_driver = maildir
mail_path = ~/Maildir
mail_index_path = /var/indexes/%{user}
mail_control_path = /var/control/%{user}

Обратите внимание и на смену синтаксиса переменных: %u уступил место форме %{user}. Если вы переезжаете с 2.3 на 2.4, это ровно то место, где конфиг тихо перестаёт делать то, что вы думали.

Второй повод развести control-файлы отдельно — квоты файловой системы. Dovecot не умеет корректно обрабатывать ошибку записи в control-файл, а именно это и происходит, когда у пользователя кончилась квота на разделе. Поэтому официальная рекомендация — держать control вне квотируемого раздела. Побочно это даёт нам ту самую защиту от find, ради которой всё и затевалось.

Но есть подвох, о котором молчат все инструкции, и он ровно про нашу тему. Если вы просто поменяете конфиг на живом сервере и перезапустите Dovecot, он не найдёт dovecot-uidlist по новому пути — и создаст новые. То есть вы получите ту же самую аварию, только по собственной воле и сразу на всех ящиках. Управляющие файлы надо физически перенести на новое место при остановленном сервисе, сохранив структуру каталогов, и только потом стартовать. Проверять — на одном тестовом ящике, сравнивая uidvalidity до и после через doveadm mailbox status.

Третий контур — бэкап. Он должен захватывать служебные файлы, а не только письма. Файловая копия каталога /var/vmail со всеми dovecot-* внутри — это то, что спасло «Дентал-Дом». Логическая выгрузка через doveadm backup полезна как второй контур (она переживает смену формата хранения и умеет в инкремент), но исходные UID из неё вернуть не получится. Держите оба, если объёмы позволяют; если приходится выбирать — файловый.

И мониторинг. Два простых датчика закрывают 90 % риска: алерт на аномальный рост исходящего трафика с почтового сервера (норма ×3 держится дольше 30 минут) и алерт на появление в mail.log строк UIDVALIDITY changed. Второе срабатывает на первом же пострадавшем ящике, но только если индекс уцелел — при удалении «всего сразу» строки не будет, поэтому добавьте третий датчик: раз в ночь сохранять doveadm mailbox status -A uidvalidity '*' и сравнивать с прошлой выгрузкой. У нас это обычный grep в связке с отправкой в Telegram — примитивно, но за полтора года дважды поймало проблему до того, как её заметили пользователи.

Проверьте прямо сегодня одну вещь: попадают ли служебные файлы Dovecot в ваш бэкап. Откройте последнюю копию и поищите в ней `dovecot-uidlist`. Если его там нет — вы не защищены от этой аварии вообще ничем.

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

Письма точно не пропали?

Точно. Удаление файлов по маске `dovecot*` не трогает сами сообщения — они лежат отдельными файлами в каталогах `cur/` и `new/`. Посчитайте их: `find /var/vmail/domain/user/Maildir/.Folder/{cur,new} -type f | wc -l`. Число должно совпадать с тем, что было. Потеряны только метаданные: соответствие UID↔файл и названия пользовательских меток.

Можно ли вернуть старые UID, если бэкапа нет?

Нет. Соответствие UID↔имя файла хранилось только в `dovecot-uidlist` и нигде не дублируется. Если файл удалён, а Dovecot успел создать новый, старые UID утрачены безвозвратно. Единственный сценарий — вернуть `dovecot-uidlist` из файлового бэкапа до того, как Dovecot откроет папку и перезапишет его.

Флаги «прочитано» и «отвечено» тоже теряются?

Нет, стандартные флаги IMAP закодированы прямо в имени файла письма — суффикс вида `:2,S` (Seen), `:2,RS` (Replied+Seen), `:2,T` (Trashed). Их удаление служебных файлов не задевает. Теряются только пользовательские метки, потому что в имени файла они обозначены буквами a–z, а расшифровка букв лежала в `dovecot-keywords`.

Как безопасно пересобрать индексы Dovecot?

Командой `doveadm force-resync -u user@domain '*'` для одного ящика. Она пересканирует папку и перестраивает индекс поверх существующих `dovecot-uidlist` и `dovecot-keywords`, не удаляя их, так что UID сохраняются. Ключ `-A` (по всем пользователям) применяйте только ночью и под `ionice -c3`: на большом хранилище это часы дисковой нагрузки.

Сколько места реально освобождает удаление индексов?

Обычно единицы процентов от объёма хранилища. На нашем стенде с 240 ГБ почты это дало 3,4 ГБ — полтора процента и двое суток последствий. Место занимают письма и вложения. Чистить надо корзины и спам (`doveadm expunge -A mailbox Trash savedbefore 30d`), архивы уволенных и включать квоты.

Как понять по логам, что случилось именно это?

Ищите в логе Dovecot строки с `UIDVALIDITY changed` — они означают, что папка получила новый UIDVALIDITY и клиенты пойдут в полный пересинк. Но такая строка появляется, только если старый `dovecot.index` пережил удаление uidlist; если по маске снесли всё сразу, лог молчит. Тогда смотрите `doveadm mailbox status -u user uidvalidity uidnext messages '*'`: `uidvalidity`, совпадающий с unix-временем минувшей ночи, и `uidnext`, равный числу писем плюс один, при большом ящике означают, что нумерация началась заново. Время создания `dovecot-uidlist` (`stat`) подтвердит момент.

Стоит ли переносить control-файлы в отдельный каталог на живом сервере?

Стоит, но строго при остановленном Dovecot и с физическим переносом существующих файлов на новое место. Если просто поменять конфиг и перезапустить сервис, он не найдёт `dovecot-uidlist` по новому пути и создаст новые — вы получите ту же аварию, только сразу по всем ящикам и по собственной инициативе.

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

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

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

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

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

Источники

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