Почтовый сервер надо было поднять в своей инфраструктуре, на Hyper-V. Установка как таковая прошла ровно и заняла меньше всего времени за весь проект.

Интереснее не она, а то, что делается до неё. У почты есть несколько условий работы, которые от вас не зависят и выясняются дорого — уже после того, как всё настроено.

Коротко

  • Gen 2, Ubuntu 22.04 LTS, 4 vCPU, 8 ГБ памяти.
  • Dynamic Memory отключил сразу: с докерной нагрузкой она даёт нестабильность.
  • До установки — внешний IP по чёрным спискам, открытый 25-й порт, PTR.
  • Все три чинятся не вашими руками, поэтому выясняются первыми.

Конфигурация

Виртуалка второго поколения, Ubuntu 22.04 LTS, 4 виртуальных ядра, 8 гигабайт памяти. Для ста сорока ящиков этого достаточно с запасом.

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

Это тот случай, когда лучше зафиксировать ресурс, чем экономить его динамически. Экономия была бы небольшой, а цена ошибки — доверие к системе, которую только что развернули.

Делается это на хосте, при выключенной виртуалке:

Set-VMMemory -VMName "mail" -DynamicMemoryEnabled $false -StartupBytes 8GB
Set-VMProcessor -VMName "mail" -Count 4

Что проверить до установки

Внешний IP по чёрным спискам. Адрес мог быть использован до вас кем угодно. Если он в списках, письма будут отвергаться, и вы будете искать причину в SPF, DKIM и настройках Postfix — то есть везде, кроме нужного места. Проверил заранее: чистый.

Проверяется запросом к списку, причём адрес записывается задом наперёд — здесь для примера 203.0.113.25:

dig +short 25.113.0.203.zen.spamhaus.org
# пустой ответ — в списке нет

И вот тут важная оговорка, которая относится не только к этой команде. Пустой ответ означает «в списке нет» только при условии, что запрос вообще дошёл. Та же пустота получится, если резолвер недоступен или имя набрано с опечаткой. Прежде чем радоваться чистому результату, стоит для сверки запросить адрес, который заведомо в списке, — и убедиться, что проверка умеет отвечать «да».

Порт 25. Многие провайдеры закрывают его по умолчанию и не сообщают об этом. Без него исходящая почта не уйдёт. Проверил: открыт.

nc -vz -w5 gmail-smtp-in.l.google.com 25

PTR-запись. Обратная зона почти всегда не в вашей власти — её заказывают у провайдера, и делается это столько, сколько делается у них. Поэтому шаг отдельный и планируется заранее, а не в день переключения.

dig +short -x 203.0.113.25     # должен вернуть mail.example.com.

Общий принцип: сначала выясняется то, что нельзя починить своими руками. Если IP грязный, порт закрыт или PTR не получить — это меняет весь план, и узнавать об этом надо до, а не после.

Установка

Сама установка — три команды:

cd /opt/mailcow_data
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized && ./generate_config.sh

generate_config.sh спросит имя почтового сервера и часовой пояс и соберёт конфигурацию. Дальше docker compose up -d поднимает набор контейнеров.

Каталог я выбрал свой, а не /opt/mailcow-dockerized из документации. Держите это в голове при чтении чужих инструкций: они все написаны под стандартный путь, и команду каждый раз придётся править под свой.

Итог

Виртуалка стабильна, mailcow установлен, дальше начиналось интересное — в частности, сертификат, который отказывался выпускаться по причине, не имеющей отношения ни к сертификатам, ни к DNS. Об этом отдельно.