Восстановили mailcow — ящики пустые в SOGo и Thunderbird, хотя место на диске занято
Restore mailcow прошёл без ошибок, диск показывает те же гигабайты, что и до аварии, а сотрудники видят пустые папки. Прежде чем паниковать и запускать восстановление заново — сначала проверьте один параметр в mailcow.conf. В девяти случаях из десяти письма никуда не делись, Dovecot просто ищет их не в том подкаталоге.
Симптом: диск занят, а в почте пусто
«Оттиск Мастер» — производство печатей и штампов на 27 рабочих мест, почта на mailcow: заказы от типографий-партнёров, переписка с дизайнерами макетов, обращения клиентов с эскизами оттисков во вложениях. Мы обслуживаем их корпоративную почту, и в феврале у хостера случился отказ диска на исходной ВМ. Восстанавливали на новом сервере из штатного бэкапа mailcow — backup_and_restore.sh restore отработал без единой ошибки, все компоненты выбраны, лог чистый.
Первая проверка после восстановления — обычно du -sh по тому с почтой, и она обманчиво успокаивает: том vmail-vol-1 занимал ровно столько же места, сколько занимал до аварии, с точностью до мегабайта. Значит, физически файлы писем скопировались все. Но когда бухгалтер открыла SOGo проверить старый счёт от поставщика бумаги — папка «Входящие» была пустой. Та же картина в Thunderbird у остальных сотрудников: аккаунт подключается, авторизация проходит, а писем ноль.
Развилка на этом месте определяет, потеряете вы полдня или будете полночи разбирать архивные бэкапы заново. Вариант первый — предположить, что restore реально не восстановил данные, и начать переигрывать процедуру с нуля, рискуя данными, которые уже накопились с момента восстановления, включая новые письма, пришедшие уже на восстановленный сервер. Вариант второй — за пять минут проверить один конкретный параметр конфигурации, прежде чем трогать что-либо ещё. Я всегда иду вторым путём, и вот почему он почти всегда закрывает вопрос.
- restore отработал без ошибок в логе — не гарантия, что почта видна в клиентах
- занятое место на диске = файлы физически скопированы
- пустые папки в SOGo/Thunderbird при этом = проблема пути поиска, а не потери данных
- первый шаг — проверка конфигурации, а не повторный restore
Матчасть: что такое MAILDIR_SUB и почему он решает всё
Dovecot в mailcow ищет письма ящика по пути /var/vmail/<домен>/<пользователь>/, к которому добавляется подкаталог из параметра MAILDIR_SUB в mailcow.conf — это видно прямо в userdb-запросе, который собирает docker-entrypoint.sh контейнера dovecot-mailcow. Документация формулирует это без обиняков: Dovecot загружает письма из указанного подкаталога тома mailcowdockerized_vmail-vol-1, и если этот параметр отличается от исходного состояния — писем в интерфейсе не будет вообще, при этом сами файлы никуда не денутся с диска.
Проблема в том, что на старых установках mailcow этот параметр был пустым или отсутствовал — то есть Dovecot искал письма прямо в каталоге ящика. А свежая установка получает его от generate_config.sh уже заполненным: скрипт пишет в новый mailcow.conf строку MAILDIR_SUB=Maildir. Формально это просто путь. Практически — если на исходном сервере параметра не было (пустое значение), а на новом он появился как Maildir, Dovecot после restore будет упорно смотреть в .../Maildir/, а все реальные файлы писем так и остались лежать уровнем выше.
Официальная документация именно поэтому выносит предупреждение отдельным блоком с заголовком «Danger for older installations» — то есть разработчики знают об этой ловушке и явно предупреждают о ней до того, как вы нажмёте restore, а не после. Проблема в том, что предупреждение читают не все, особенно когда восстановление делается в спешке посреди инцидента, как это было у «Оттиск Мастера» — сервер лежал, клиенты ждали, читать changelog построчно было не до того.
- `MAILDIR_SUB` в mailcow.conf — подкаталог тома vmail-vol-1, где Dovecot ищет письма
- не задан или пуст = Dovecot ищет прямо в каталоге ящика `/var/vmail/<домен>/<пользователь>/`
- задан как `Maildir` (или любое другое значение) = ищет только в этом подкаталоге
- несовпадение старого и нового значения — письма «пропадают» из интерфейса, оставаясь на диске
Как проверить и починить: сравнение старого и нового mailcow.conf
Первым делом я поднял архивную копию mailcow.conf с упавшего сервера (она у нас всегда лежит отдельно от самого бэкапа, в рамках регламента резервного копирования) и сравнил значение параметра построчно с новым конфигом:
grep MAILDIR_SUB /path/to/old-backup/mailcow.conf
grep MAILDIR_SUB mailcow.confУ «Оттиск Мастера» старый конфиг вообще не содержал строки MAILDIR_SUB — параметр отсутствовал полностью, то есть письма изначально лежали в корне тома. Новый конфиг после развёртывания на чистом сервере содержал MAILDIR_SUB=Maildir — generate_config.sh ставит это значение всем новым установкам. Ровно то расхождение, которое документация называет главной опасностью восстановления на старых инсталляциях: если в исходной установке значение не задано, в новой конфигурации его нужно либо не задавать вообще, либо явно удалить, если оно там уже появилось.
Правка простая — закомментировать или удалить строку MAILDIR_SUB=Maildir в mailcow.conf, затем пересоздать контейнер dovecot: docker compose up -d dovecot-mailcow. Проверка заняла минуту: после пересоздания контейнера обновил страницу SOGo — вся переписка на месте, письма от февраля и раньше видны с правильными датами. Старые файлы трогать не пришлось. Но была одна тонкость, о которой почти не пишут: за три часа между restore и правкой на сервер пришло около сорока новых писем, и Dovecot честно сложил их в новый подкаталог Maildir/ — после возврата пустого значения они «пропали» уже из интерфейса. В треде «Cannot access old/migrated mails» описан ровно этот эффект наоборот: новые письма видны, старые нет. Эти письма я перенёс вручную из Maildir/cur и Maildir/new в соответствующие каталоги ящика с сохранением владельца vmail и потом выполнил docker compose exec dovecot-mailcow doveadm force-resync -u user@example.com '*' по каждому затронутому ящику.
- сравнить `MAILDIR_SUB` в старом и новом mailcow.conf построчно
- если в старом не было — в новом не задавать / удалить
- если в старом было конкретное значение — в новом должно быть идентичным
- после правки — пересоздать контейнер: `docker compose up -d dovecot-mailcow`, файлы не трогать
Это не единичный случай: два похожих разбора с форума mailcow
История «Оттиск Мастера» не уникальна — в официальном сообществе mailcow есть минимум два независимых разбора с тем же корнем проблемы. В обсуждении «Issue after migration» администратор столкнулся с точно такой же картиной: письма физически на месте, но невидимы после переноса на новый сервер. Разбор показал ту же причину — несовпадение MAILDIR_SUB, у старой установки пустое значение, у новой — Maildir; возврат к пустому значению вернул письма в интерфейс.
Второй тред, «Cannot access old/migrated mails», независимо подтверждает тот же симптом и то же решение на другой инсталляции — то есть это не разовый баг конкретной версии, а системная ловушка самого механизма восстановления, о которой явно предупреждает официальная документация именно потому, что она воспроизводится у разных людей на разных серверах. Официальная документация по миграции (docs.mailcow.email/maintenance/migration/) отдельно рекомендует переносить mailcow.conf со старого сервера на новый как есть, а не создавать его заново с нуля — это исключает саму возможность расхождения MAILDIR_SUB.
Практический вывод для восстановления после аварии, когда старого сервера уже физически нет: если у вас сохранилась хотя бы копия mailcow.conf (в бэкапе, в git, в архиве настроек) — переносите его значения, а не полагайтесь на значения по умолчанию свежей установки. Если копии конфига нет вообще — это повод в первую очередь проверить, действительно ли на диске лежит Maildir/ как подкаталог, командой docker compose exec dovecot-mailcow ls -la /var/vmail/domain.ru/user/ и сверить с фактической структурой, прежде чем менять параметр вслепую.
- «Issue after migration» — тот же симптом, та же причина: MAILDIR_SUB пустое vs Maildir
- «Cannot access old/migrated mails» — независимое подтверждение на другой инсталляции
- документация по миграции рекомендует переносить mailcow.conf целиком, не создавать заново
- нет копии старого конфига — сверяйте фактическую структуру каталогов на диске перед правкой
Как правильно выполнить restore, чтобы не попасть в эту ловушку
Штатная процедура восстановления в mailcow запускается на уже поднятой и пустой инсталляции: ./helper-scripts/backup_and_restore.sh restore, дальше скрипт интерактивно просит путь к бэкапам (/opt/backup, например), показывает список найденных точек восстановления с датами и позволяет выбрать, какие компоненты восстанавливать — весь бэкап целиком или отдельные части: Crypt data, Rspamd data, каталог почты, Redis, Postfix, SQL. Для полного восстановления после аварии я всегда беру пункт «all», а не собираю восстановление по частям вручную — риск забыть компонент и получить рассинхронизацию между базой и файлами выше, чем экономия времени.
Отдельный нюанс, о котором нужно знать заранее: если бэкап делался на одной архитектуре процессора (например, x86), а восстанавливаете вы на другой (ARM64), скрипт сам обнаружит несовместимость и предложит пропустить восстановление данных Rspamd — это ожидаемое поведение, а не ошибка, и Rspamd просто соберёт свою статистику заново с нуля.
И последнее правило, которое я довёл до автоматизма после кейса «Оттиск Мастера»: перед восстановлением на новом сервере я сначала поднимаю пустой mailcow тем же способом, что и исходный (docker compose up -d), и только потом запускаю restore поверх него, предварительно скопировав mailcow.conf со старого сервера или сверив вручную ключевые параметры — MAILDIR_SUB в их числе, но не единственный. Это на порядок надёжнее, чем чинить расхождения постфактум по жалобам сотрудников.
Отдельно фиксирую в регламенте обслуживания клиента, где именно лежит архивная копия mailcow.conf и с какой периодичностью она обновляется — после каждого изменения конфигурации вручную, не по расписанию раз в месяц. Для «Оттиск Мастера» мы сейчас храним копию конфига в зашифрованном приватном репозитории отдельно от бэкапов почты и от самого сервера mailcow, именно чтобы не оказаться в ситуации «сервер упал, бэкап почты есть, а сверить MAILDIR_SUB не с чем».
- `./helper-scripts/backup_and_restore.sh restore` — запускать на уже поднятой пустой инсталляции mailcow
- восстанавливать «all», а не части по отдельности, если это не осознанный частичный recovery
- смена архитектуры (x86 → ARM64) — Rspamd не восстановится, это ожидаемо
- до restore — сверить mailcow.conf со старым сервером, MAILDIR_SUB в первую очередь
Когда причина другая: не путайте с шифрованием и служебными файлами
MAILDIR_SUB — не единственная причина «пустых» ящиков после операций с почтовым хранилищем mailcow, и важно уметь отличить её от двух похожих на первый взгляд, но совсем других по природе проблем. Первая — шифрование писем через mail_crypt. Если на исходном сервере оно было включено, простое копирование файлов vmail без соответствующих EC-ключей делает содержимое писем нечитаемым в принципе, а не невидимым — это принципиально другая история, я разбирал её отдельно в тексте про архив vmail и ключи шифрования. Отличить один случай от другого просто: при проблеме с MAILDIR_SUB письма после правки конфига появляются мгновенно и полностью читаемы; при отсутствующих ключах шифрования doveadm и клиенты будут либо давать ошибку доступа, либо показывать нечитаемую кашу вместо текста.
Вторая соседняя история — повреждённые или удалённые служебные файлы Dovecot вроде dovecot-uidlist и dovecot-keywords. Это не про restore mailcow, а про ситуацию, когда служебные файлы конкретной папки стёрты или испорчены руками администратора или сторонним скриптом резервного копирования, который случайно задел служебные файлы вместе с письмами. В таком случае папка не пустая, но клиенты перекачивают всю почту заново и теряют метки прочитанности — симптом внешне похож на «сломанную почту после операции», но причина и лечение другие, там всё описано в разборе про служебные файлы Dovecot.
Практическое правило разделения для дежурного администратора: если это именно restore на новый сервер и папки пусты полностью — сначала MAILDIR_SUB. Если папки читаются наполовину криво, часть писем в порядке, а часть выдаёт ошибки — смотрите на ключи шифрования. Если ни restore, ни миграции не было, а «сломалось само» после ручной чистки диска — это, скорее всего, повреждённые control-файлы Dovecot, а не MAILDIR_SUB. На практике первый вопрос, который я задаю дежурному инженеру перед тем как открывать конфиг: было ли восстановление или перенос на новый сервер в последние сутки. Если да — MAILDIR_SUB проверяется раньше любых других гипотез, потому что стоит один grep и не требует прав на изменение чего-либо, кроме одной строки конфига.
- пустые ящики сразу после restore на новый сервер → MAILDIR_SUB, проверять первым
- письма нечитаемы (не пустые, а «каша») → отсутствуют ключи mail_crypt
- почта перекачивается заново, метки прочитанности слетели → повреждены control-файлы Dovecot
- три разных симптома — три разных причины, не лечить одно как другое
Частые вопросы
После восстановления mailcow ящики пустые, но место на диске занято — письма потеряны?
Скорее всего нет. Это классический симптом несовпадения параметра MAILDIR_SUB в mailcow.conf между старой и новой установкой: Dovecot ищет письма в другом подкаталоге тома vmail-vol-1, чем там, где они физически лежат. Сравните MAILDIR_SUB в старом и новом конфиге и приведите к одному значению.
Что такое MAILDIR_SUB в mailcow.conf?
Параметр, задающий подкаталог внутри тома vmail-vol-1, откуда Dovecot загружает письма. Если он не задан — Dovecot ищет прямо в каталоге ящика /var/vmail/<домен>/<пользователь>/. Если задан, например, как Maildir — ищет только в этом подкаталоге. Несовпадение значения между старой и новой установкой после restore делает письма невидимыми в интерфейсе при том, что файлы остаются на диске.
Как проверить MAILDIR_SUB перед восстановлением на новом сервере?
Сравните значение в архивной копии mailcow.conf со старого сервера и в конфиге новой установки: grep MAILDIR_SUB mailcow.conf для обоих файлов. Если в старом параметр отсутствовал — не задавайте его и в новом (или удалите, если он там появился по умолчанию), затем пересоздайте контейнер dovecot-mailcow.
Чем проблема с MAILDIR_SUB отличается от потери писем из-за шифрования mail_crypt?
При несовпадении MAILDIR_SUB письма после правки конфига появляются мгновенно и полностью читаемы — файлы были на месте, менялся только путь поиска. При отсутствии ключей шифрования mail_crypt письма либо не открываются вовсе, либо отображаются нечитаемой кашей — это проблема доступа к содержимому, а не пути к файлам, и лечится она иначе.
Нужно ли пересоздавать индекс или базу данных после правки MAILDIR_SUB?
Нет, в типовом случае достаточно поправить значение в mailcow.conf и выполнить docker compose up -d dovecot-mailcow, чтобы контейнер Dovecot переподхватил конфигурацию. Файлы писем при этом не трогаются и не переиндексируются принудительно — Dovecot просто начинает смотреть в правильный подкаталог.
Источники
- mailcow docs: Restore — Danger for older installations — Требование сверить MAILDIR_SUB перед восстановлением на новый сервер, дословное предупреждение: если параметр не был задан в старой установке, не задавать его в новой, иначе письма не отобразятся. https://docs.mailcow.email/backup_restore/b_n_r-restore/
- mailcow docs: Restore — процедура backup_and_restore.sh — Синтаксис ./helper-scripts/backup_and_restore.sh restore, выбор точки восстановления и компонентов (0 — all), поведение при несовместимой архитектуре для Rspamd. https://docs.mailcow.email/backup_restore/b_n_r-restore/
- mailcow community: After update — maildir_sub is not set properly — Разобранный кейс: старое MAILDIR_SUB= (пусто), новое MAILDIR_SUB=Maildir — несовпадение объясняет исчезновение писем из интерфейса при физически целых файлах. https://community.mailcow.email/d/5468-after-update-maildir-sub-is-not-set-properly
- mailcow community: Issue after migration — Независимый разбор той же проблемы после переноса на новый сервер, решение — вернуть пустое значение MAILDIR_SUB. https://community.mailcow.email/d/607-issue-after-migration
- mailcow docs: Migration — Рекомендация переносить mailcow.conf со старого сервера на новый целиком, а не создавать заново, чтобы исключить расхождение параметров вроде MAILDIR_SUB. https://docs.mailcow.email/maintenance/migration/



