Набор команд ниже я собрал, пока разбирал другую задачу — почему у пользователей в Thunderbird часть папок оказалась пустой. Сервер тогда был ни при чём, но научиться смотреть на IMAP-сессии пришлось, и пригодилось потом.

Выкладываю как рабочий набор: что показывает срез, что показывает движение, и одна грабля, на которую я наступил по дороге.

Коротко

  • doveadm who — кто подключён и сколько сессий на ящик.
  • Шесть и пятнадцать подключений на ящик — это офисный NAT, а не аномалия.
  • Старые PID — долгоживущие IDLE-сессии, так и должно быть.
  • docker compose logs -f без имени сервиса выдаёт много текста и ни строчки Dovecot.

Срез: кто вообще подключён

Первое, что показывает картину целиком — doveadm who:

docker compose exec dovecot-mailcow doveadm who
username               # proto (pids)                       (ips)
otdel@example.com      6 imap  (1021 1188 2210 ...)          (198.51.100.7)
it@example.com        15 imap  (344 351 402 ...)             (198.51.100.7)

У меня там обнаружилось шесть одновременных подключений на одном ящике и пятнадцать на другом. Выглядит тревожно ровно до того момента, пока не посмотришь на адреса: все из-за офисного NAT, то есть с одного внешнего IP. Это не атака и не поломка, это один офис.

У части процессов оказались очень старые PID. Это тоже нормально: долгоживущие сессии в режиме IDLE так себя и ведут — клиент держит соединение открытым, чтобы мгновенно получать новые письма, и не переподключается неделями.

Обе эти детали стоит знать заранее, иначе первый же взгляд на doveadm who рождает ложную тревогу. Много сессий с одного адреса и старые процессы — это описание нормально работающего офиса, а не диагноз.

Движение: что происходит прямо сейчас

Срез отвечает на вопрос «кто», но не на вопрос «чем занят». Для этого другие инструменты.

verbose_proctitle. С ним в обычном ps видно, какую команду процесс выполняет в данный момент. Дешёвый способ понять, кто из сессий реально работает, а кто просто висит.

Счётчики /proc/<pid>/io. Разница значения wchar, снятая дважды с интервалом, показывает, идёт ли реальная передача данных:

docker compose exec dovecot-mailcow sh -c \
  'grep wchar /proc/1188/io; sleep 10; grep wchar /proc/1188/io'

Два числа выросли — процесс работает. Не выросли — висит. Это ответ на вопрос «завис или медленно работает», на который взгляд в ps не отвечает никогда.

doveadm mailbox status. Состояние ящика по папкам — сколько сообщений и сколько занимают:

docker compose exec dovecot-mailcow doveadm mailbox status \
  -u otdel@example.com "messages vsize" '*'

Полезно, когда надо сравнить то, что видит сервер, с тем, что видит клиент. Именно это сравнение и показало в моём случае, что сервер отдаёт письма, а клиент их не показывает.

verbose_proctitle и rawlog_dir включаются в data/conf/dovecot/extra.conf:

verbose_proctitle = yes
protocol imap {
  rawlog_dir = /tmp/rawlog/%u
}

rawlog — тяжёлая артиллерия: полный дамп протокола, каждая команда IMAP и каждый ответ. После него вопросов «а что клиент вообще просил» не остаётся.

Но с ним есть условие, которое нельзя пропускать: в дамп попадает содержимое писем. То есть на диске появляется незашифрованная копия чужой переписки. Включать — только на время отладки, только на конкретного пользователя, и удалять сразу после. Это тот случай, когда забыть выключить диагностику дороже, чем не включать её вовсе.

Логика набора такая: сначала срез, потом движение, потом протокол. Каждый следующий шаг дороже предыдущего и включается только тогда, когда предыдущий не ответил.

Грабля с логами

А теперь то, на чём я честно потерял время.

docker compose logs -f                                # мешанина nginx, ofelia, rspamd
docker compose logs -f --tail=200 dovecot-mailcow     # правильно

Первая команда вывалила много текста. Логов Dovecot там не было ни строчки.

Правильно — указывать сервис явно. Либо смотреть лог через Redis, ключ DOVECOT_MAILLOG.

Случай мелкий, но показательный, и я записал его себе отдельно. Команда отработала без ошибки и выдала много текста, а ответа на мой вопрос в этом тексте не было. Это худший вид обратной связи: молчание хотя бы честно, а поток нерелевантных строк выглядит как результат и заставляет их читать.

Общее правило, которое я из этого вывел: если инструмент выдал много данных, первым делом надо проверить, что среди них есть хотя бы одна строка того типа, который вы ищете. Не «есть ли ответ», а «есть ли вообще нужный источник в выводе». Это две разные проверки, и вторая дешевле.