Поднимаем свой почтовый сервер

Зачем это

Несмотря на наличие Telegram, пушей и других каналов уведомлений, электронная почта по-прежнему нужна:

  1. Богатое форматирование — HTML-письма, вложения, таблицы, баннеры, вложения Telegram с Markdown близко не стоит (upd: Телеграм недавно выкатил поддержку markdown, но я ее не смотрел пока еще в плане API, а в пользовательском очень странно работает).
  2. Сервисы завязаны на email — регистрация, сброс пароля, отчёты, алерты из Zabbix, счета от контрагентов.

Публичные провайдеры при этом закручивают гайки (вместо пруфов статья на хабре):

  • Mail.ru отключил бесплатную SMTP-отправку для внешних приложений
  • Yandex 360 урезал бесплатный тариф, SMTP под паролями приложений работает нестабильно
  • Платные тарифы — 200–400 ₽/мес. за почтовый ящик, и это только почта

Плюс вопросы конфиденциальности и риски чебурнета: если домен на российском регистраторе и почта на зарубежном сервере — потеря доступа вполне реальна.

Решение: свой почтовый сервер на своём железе с собственным доменом.

Для меня это началось как проект вечера: нужно было отправить показания счётчиков в ТСЖ по email, а Google relay в очередной раз сломался. Планировал поднять минимальный SMTP-релей — просто чтобы письма уходили. Но в процессе втянулся и развернул полноценный почтовый сервер всё-в-одном. Ранее для на корпоративном сервере настраивал связку exim + postfix, но желания повторять все это дома не было, тем более, что хотелось LDAP, а на это прям много времени потратил ранее.


Что требуется

# Требование Подробнее
1 Интернет Стабильный. Некоторые ISP блокируют исходящий порт 25 — проверьте заранее (telnet smtp.gmail.com 25)
2 Статичный IP Динамический IP = меняющаяся репутация. Большинство крупных получателей отклоняют письма с динамических адресов
3 PTR-запись Обратная DNS-запись IP → hostname. Выдаётся провайдером, должна указывать на ваш почтовый домен (mail.example.com)
4 Домен Свой домен для MX-записи. Выбирать.ru/.рф если делать полностью автономно, но такие домены надо регистрировать дополнительно с сентября
5 Среда запуска Docker/Podman, VM или bare metal. 512 MB RAM минимум для Stalwart, 2+ GB для тяжёлых стеков
6 Базовые скиллы DNS, Linux CLI, понимание что такое MX/SPF/DKIM/DMARC

Как работает почта

Коротко о компонентах:

  • MTA (Mail Transfer Agent) — передаёт письма между серверами по SMTP. Примеры: Postfix, Exim.
  • MDA (Mail Delivery Agent) — раскладывает письма по ящикам пользователей.
  • MUA (Mail User Agent) — почтовый клиент: Thunderbird, Apple Mail, K-9.
  • Антиспам — фильтрует входящие (Rspamd, SpamAssassin).

Путь письма:

Отправитель (MUA)
  → SMTP отправителя (порт 587/465)
  → MTA отправителя
  → MTA получателя (порт 25, по MX-записи)
  → MDA → ящик
  → MUA получателя (IMAP/POP3)

Ключевые DNS-записи:

Запись Назначение
MX Куда доставлять почту для домена
SPF Какие IP имеют право отправлять от имени домена
DKIM Криптографическая подпись письма
DMARC Политика обработки писем, не прошедших SPF/DKIM
PTR Обратная запись: IP → hostname

Какие серверы бывают

Проект Стек RAM Сложность Веб-почта Особенности
Mailcow Postfix + Dovecot + Rspamd + SOGo ~2 GB Средняя SOGo (встроен) Полный фарш, удобный UI, только Docker
Mail-in-a-Box Postfix + Dovecot + Roundcube ~1 GB Низкая Roundcube (встроен) Ставится за 20 минут, мало гибкости
iRedMail Postfix + Dovecot + Amavis ~1 GB Средняя Roundcube + SOGo (встроены) Проверенный, есть платный Pro
Mailu Postfix + Dovecot + Rspamd ~500 MB Низкая Roundcube (встроен) Простая настройка через .env, Docker
Poste.io Haraka + Dovecot ~300 MB Низкая Встроенный Быстрый старт, часть функций только в платной версии
Maddy Всё в одном (Go) ~200 MB Высокая Нет Лёгкий, конфиг неочевидный
Stalwart Всё в одном (Rust) ~100 MB Средняя Нет (нужен Roundcube/Snappymail отдельно) Современный, JMAP, хороший веб-UI администратора

Почему Stalwart:

  • Написан на Rust — быстрый, экономный по памяти
  • Всё в одном бинарнике: SMTP, IMAP, JMAP, ManageSieve
  • Встроенный антиспам без дополнительных компонентов
  • Адекватный веб-интерфейс администратора
  • Активная разработка и хорошая документация
  • Единственный, который поднялся у меня в докере за NATом с полпинка
  • В новой версии очень сильно прокачали работу с секретами, для корпоративных или околокорпоративных клиентов будет плюсом

Почему не Stalwart

  • В процессе перехода на новую версию я плюнул создал все заново т.к. миграция данных очень сложная и может сломаться на любом шаге
  • В нем нет веб клиента, я для теста ставил рядом Roundcube, но есть сервера, где веб клиент идет из коробки

Ограничения бесплатной (Community) редакции Stalwart:

Community-редакция распространяется под AGPL-3.0 и бесплатна без ограничений по числу писем или ящиков, но часть функций доступна только в платной Enterprise-редакции (сравнение редакций):

  • Нет мультитенантности — управления несколькими организациями/доменами с отдельными квотами в одном инстансе
  • Нет sharded/распределённого хранилища и read-реплик (актуально только для больших инсталляций)
  • Нет дашборда с real-time статистикой и алертами по метрикам (email/webhook)
  • Нет истории доставки сообщений (message delivery history)
  • Нет архивации и восстановления удалённых писем (account archiving, un-deletion)
  • Нет masked email (приватные алиасы)
  • Нет AI/LLM-классификатора спама и AI-функций в Sieve-скриптах
  • Нет brand-кастомизации календарных приглашений и уведомлений
  • Поддержка только community (GitHub Issues, Discord, форум) — платный Premium Support доступен от 150 почтовых ящиков
  • Цена Enterprise: от €2/ящик/год (25–499 ящиков), до €0.89 при 50 000+ (прайсинг)

Но частично ограничения бесплатной версии можно обойти своими дашбордами в Grafana

Разворачиваем Stalwart

1. Установка

Docker Compose — самый удобный вариант:

services:
  stalwart:
    image: stalwartlabs/stalwart:latest
    container_name: stalwart
    restart: unless-stopped
    ports:
      - "25:25"     # SMTP входящий
      - "465:465"   # SMTPS
      - "587:587"   # SMTP submission
      - "143:143"   # IMAP
      - "993:993"   # IMAPS
      - "8080:8080" # Web UI
    volumes:
      - ./data:/opt/stalwart
docker compose up -d
docker compose logs stalwart | grep -i password

При первом запуске в логах появится временный пароль администратора — сохраните.

2. TLS-сертификат

Без TLS современные серверы отклоняют соединение — настраиваем сразу после запуска.

Stalwart поддерживает автоматическое получение Let’s Encrypt. Перейдите в Settings → TLS и укажите домен (mail.example.com). Сертификат выпустится и будет обновляться автоматически.

Порт 8080 (Web UI) тоже стоит закрыть снаружи или перевести на HTTPS после первоначальной настройки.

Я не настраивал это т.к. у меня выпуском сертификатов занимается свой бот, при

3. Настройка DNS

Добавьте в зону вашего домена (example.com):

; MX
@                    MX   10  mail.example.com.

; A-запись сервера
mail                 A        <ваш IP>

; SPF — разрешаем только наш сервер
@                    TXT      "v=spf1 mx ~all"

; DKIM — публичный ключ берём из веб-UI Stalwart
default._domainkey   TXT      "v=DKIM1; k=rsa; p=<ключ из UI>"

; DMARC
_dmarc               TXT      "v=DMARC1; p=quarantine; rua=mailto:postmaster@example.com"

DKIM-ключи генерируются автоматически — скопируйте значение из Settings → Domains → DKIM в веб-интерфейсе.

Новые версии Stalwart поддерживают автоматическое обновление DNS-записей (включая DKIM) без ручного копирования. Достаточно прописать API-креды от вашей DNS-панели в Settings → DNS Provider — сервер сам создаст и обновит нужные записи.

4. PTR-запись

Зачем нужна PTR: это обратная DNS-запись — по IP-адресу можно узнать имя хоста. Принимающий сервер проверяет: «этот IP утверждает, что отправляет почту от mail.example.com — а PTR у него точно указывает на mail.example.com?» Если PTR нет или не совпадает — письмо сразу идёт в спам или отклоняется. Это один из базовых признаков легитимности сервера.

Обращаетесь к провайдеру (или настраиваете в личном кабинете, если ISP это позволяет) и просите создать PTR-запись:

<ваш IP>  →  mail.example.com

Провайдер обычно делает это быстро — в моём случае ответили и выставили в тот же день. До появления PTR письма стабильно попадали в спам, после — стали доходить нормально.

Проверка: dig -x <ваш IP>

Ответ должен содержать mail.example.com.

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

5. Постмастеры

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

Зарегистрируйтесь в:

Все четыре требуют подтверждения домена через DNS TXT-запись.

Что показывают постмастеры:

  • Репутацию домена и IP (хорошая / удовлетворительная / плохая / критическая)
  • Процент жалоб на спам
  • Процент ошибок доставки
  • Общую статистику по письмам

Если репутация падает до «плохой» — Gmail и Outlook начинают массово отправлять почту в спам или отклонять её. Статистика появляется не сразу, а после накопления достаточного объёма писем (несколько сотен).

6. Проверка

# SMTP-тест отправки
swaks --to test@gmail.com --from you@example.com \
  --server mail.example.com:587 \
  --auth LOGIN --auth-user you@example.com

Онлайн-инструменты:

  • mail-tester.com — общий рейтинг письма (SPF/DKIM/DMARC/контент)
  • mxtoolbox.com — диагностика DNS-записей
  • dig TXT default._domainkey.example.com — проверить DKIM в DNS

Что должно быть в заголовках полученного письма:

Authentication-Results: spf=pass; dkim=pass; dmarc=pass

И в интерфейсе stalwart

Создаем домен, указываем сам домен, псевдомнимы
Tenant и Directory не доступны в бесплатной версии


Catch-All Address указывает адрес, на который будут скидываться все письма для неизвестных ящиков
Sub-Addressing это когда вы можете для ящика user@example.com на разных площадках указывать адрес user+blah@example.com тем самым или идентифицируя отправителя (утечку) или насоздавать кучу аккаунтов на один email (естественно, в тестовых целях, я никому не советую использовать этот функционал в плохих целях)

Настройка DKIM (крипта для того, чтобы письма не помечались как спам)

TLS сертификат (можно сразу выпустить через LE)


Настройка DNS (если указать провайдера, то stalwart сам будет обновлять записи)

Куда слать репорты (далее покажу)

Отчеты

Другие сервера будут присылать отчеты безопасности на сервисный аккаунт и их можно просмотреть,но если раньше это был красивый отчет, то сейчас просто визуализация файла

Аккаунты пользователей

Я использую Authentik, поэтому решил подключить ldap


Ну и дальше ряд настроек специфичных для моей сети
В Authentik надо создать ldap outpost и настроить пользователей

Параметры подключения

Протокол Сервер Порт Шифрование
IMAP (входящие) mail.example.com 993 SSL/TLS
SMTP (исходящие) mail.example.com 587 STARTTLS

Логин — полный email (user@example.com), пароль — тот, что задали при создании аккаунта.

Thunderbird

Файл → Добавить учётную запись → Электронная почта. Thunderbird попробует настроить автоматически по домену — если не получится, вводите параметры выше вручную.

Мобильные клиенты

Настройки идентичны: K-9 Mail (Android), Canary Mail или Apple Mail (iOS/macOS) — протоколы и порты те же.

PVE

В следующих комментариях напишу про настройку rundcube и еще полезностях

И здесь есть интересная деталь : Bulwark сейчас фактически один из наиболее естественных webmail-клиентов для Stalwart, потому что он ориентирован непосредственно на Stalwart и работает через JMAP, а не через старую связку IMAP/SMTP. На форуме Stalwart его прямо рекомендуют как webmailer, созданный для Stalwart
Год назад ставил Stalwart как кластер из трёх нод — (это прямо рекомендуемый базовый вариант для небольшого HA-развёртывания) на базе Датацентров Henzler
Не пошло в “серию” - там FoundationDB очень чувствительная к задержкам в каналах (разница в десяток миллисекунд - что норма между дата-центрами- вызывает нестабильность в работе) - но если рядом и свои серверы - то вполне достойный вариант.

Вот это интересно, надо будет поставить и посмотреть.
У меня для сервисных аккаунтов roundube используется для для тестирования и настройки Sieve скриптов
Я по JMAP когда искал нашел Parula, но он оказался очень сырым, а вот Bulwark почему-то пропустил, хотя, вот тут он присутствует

Он такой прикольный, веселенький. Мне нравится, честно говоря.
Мультиаккаунт - Он позволяет, это понятно…
Мультисервер - Bulwark поддерживает направление одного развёртывания на несколько JMAP-серверов - И вот это действительно хорошо.Я на одном агенте, на маке, подключил два разных сервера совершенно в разных местах c разными “почтами” и это хорошо и удобно. НО - необходимость обновления до 1.7.4— не ради самой версии, а ради этой конкретной возможности

Развернул уже, поставил, хотел запустить на /webmail пути, чтобы запустить на одном домене со stalwart и сразу решить проблему с cors, но решил пока без пересборки обойтись.

Понравилась глубокая интеграция со stalwart

Из минусов я бы выделил классическую проблему с множеством переносов и скроллов при включении русского языка

Но вот этот клиент меня заставил задуматься над тем, чтобы развернуть где-то еще один инстанс stalwart и уйти от гугла, попытаться настроить полноценный OIDC на Authentik

По веб клиентам

Я бы выделил 2 основных
Roundcube

services:
  roundcubemail:
    environment:
      - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mail.domain.com
      - ROUNDCUBEMAIL_SMTP_SERVER=ssl://mail.domain.com
      - ROUNDCUBEMAIL_DEFAULT_PORT=993
      - ROUNDCUBEMAIL_SMTP_PORT=465
      - ROUNDCUBEMAIL_PLUGINS=archive,zipdownload,managesieve
      - ROUNDCUBEMAIL_INSTALL_PLUGINS=true
    ports:
      - 8888:80
    volumes:
      - ./data:/var/roundcube/db
      - ./config:/var/roundcube/config
    image: roundcube/roundcubemail
    restart: unless-stopped



Мне интересен был в том числе как редактор sieve скриптов (это когда сервер сам фильтрует письма без участия клиента)

Из минусов выделил бы базовый функционал без излишеств, gmail нас в этом плане разбаловал

Bulwark

services:
  bulwark:
    image: ghcr.io/bulwarkmail/webmail:latest
    container_name: bulwark
    ports:
      - "3000:3000"
    environment:
      JMAP_SERVER_URL: https://mai.domain.com
      NEXT_PUBLIC_LOCALE_PREFIX: true
      TZ: Europe/Moscow
    env_file: .env
    volumes:
      # Encrypted user settings (SETTINGS_DATA_DIR).
      - ./settings:/app/data/settings
      # Admin configuration: config.json, policy.json, admin.json (passwordHash),
      # plugins, themes, branding uploads (ADMIN_CONFIG_DIR). Can be mounted
      # read-only after running the setup wizard - append `:ro` to lock it.
      - ./admin:/app/data/admin
      # Admin runtime state: admin-state.json (login timestamps), audit.log,
      # setup token (ADMIN_STATE_DIR). Always read-write.
      - ./admin-state:/app/data/admin-state
      # Anonymous telemetry: instance id, consent state, login HMACs (TELEMETRY_DATA_DIR).
      # Persisting this preserves the admin's consent choice and stable instance id across upgrades.
      - ./telemetry:/app/data/telemetry
    healthcheck:
      test:
        [
          "CMD",
          "wget",
          "--no-verbose",
          "--tries=1",
          "--spider",
          "http://127.0.0.1:3000/api/health",
        ]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    restart: unless-stopped

Это клиент стилизованный под stalwart и разрабатываемый под него же (но не обязательно именно со stalwart использовать, можно и сдругими)


Тут уже есть

  • JMAP для современного взаимодействия с почтовым сервером (на замену SMTP и IMAP)
  • Современный и стильный дизайн
  • Веб пуши при получении писем
  • Работа с календарем, контактами, файлами
  • Можно включить stalwart специфичные фишки типа автоответа в отпуске
  • Тоже умеет редактировать sieve скрипты

Это уже если не веб gmail, то близко с этому, уже можно использовать в том числе для совместной работы

Ну и прям напрашивается, но этогонет из коробки и надо собирать образ под себя: запуск Bulwark на одном домене со Stalwart и автоматическое перенаправление с корня на веб почту, а для админки надо уже вводить /admin руками или добавить ссылку в bulwark

Я как-то интересовался темой развертывания своего почтового сервера. Тогда во всех статьях и во всех видео все хором говорили, что это не очень хорошая идея, т.к. почтовые протоколы живут со времен, когда понятия “информационная безопасность” еще даже не существовало. И из-за этого якобы требуется развертывание усиленной инфраструктуры безопасности вокруг сервера. И у крупных провайдеров типа гугла/яндекса она есть, а вот самохостятам такое развернуть - сложно и геморно, поэтому лучше во все это не лезть.

Как Ваша схема решает эту проблему? И есть ли проблема вообще? Я так понимаю, какой-нибудь crowdsec тут не прикрутить, остается только полагаться на встроенный антиспам-фильтр.

Технологии не стояли на месте Основная проблема сейчас — это не взлом вашего сервера, а скорее жесткая политика крупных провайдеров (Google, Yandex, Apple, Microsoft) по борьбе со спамом - SPF DKIM DMARC … да это все нужно настраивать - но не Бином Ньютона в самом деле - если возиться в “кайф” - то вполне вполне можно … 10 из 10 по почте получить можно и не настолько сложно и современные пакеты типа MailCow или Stalwart достаточно продуманные и навороченные
Нет таких вершин которые НЕ смогли бы взять - САМОХОСТЕРЫ

Долгое время ящик был чистый, но недавно где-то засветился и посыпался спам в огромных количествах, так я раньше думал, что гугл и прочие за счет огромной клиентской базы могут качественно фильтровать спам,но stalwart показал себя с очень хорошей стороны и пока что не пропустил ни одного письма, я считаю, что это успех

Я бы здесь рассматривал вопрос с нескольких сторон.

1. Безопасность самих протоколов.
SMTP исторически проектировался не как защищённый протокол: изначально не было ни нормального шифрования, ни аутентификации, ни проверки подлинности отправителя. Плюс сама федеративная модель почты означает, что мы вынуждены взаимодействовать с огромным количеством чужих серверов. В результате за десятилетия вокруг SMTP/IMAP накопился целый набор «заплаток» — STARTTLS, SPF, DKIM, DMARC, MTA-STS, DANE и т. д.

И вот здесь современные серверы вроде Stalwart довольно сильно меняют ситуацию: часть этих механизмов он умеет брать на себя и автоматически настраивать/контролировать. Причём некоторые вещи реализованы там уже более современно, чем во многих крупных почтовых системах.

2. Безопасность непосредственно почтового ПО.
Тут я бы вообще не переносил опыт условного Exchange 10–15-летней давности на современный софт. Старые почтовые системы тащили за собой огромное количество legacy, и тот же Exchange регулярно становился целью довольно серьёзных эксплойтов.

Stalwart в этом отношении интересен именно тем, что это современная реализация без такого исторического багажа; проект недавно фактически прошёл через очень серьёзную переработку. Настолько серьёзную, что при миграции мне даже не удалось нормально перенести свои данные.

Плюс у него уже есть собственные механизмы фильтрации/блокировки IP и защиты от brute-force. Если сверху добавить CrowdSec, а сам сервер вообще не выставлять непосредственно в домашнюю сеть и поместить в DMZ, получается уже вполне приличная многоуровневая схема.

3. Антиспам — это отдельная задача.
Про неё я уже писал выше. Здесь можно использовать Rspamd и прочие специализированные решения, а при желании даже платные варианты с LLM-анализом содержимого писем. Это уже скорее вопрос качества фильтрации, а не базовой безопасности сервера.

Что касается моей конкретной схемы, я бы выделил несколько вещей:

  • почтовый сервер держать в DMZ;
  • CrowdSec у меня уже работает на роутере и использует их threat-intelligence lists; анализ логов самого Stalwart пока не подключал, хотя у Stalwart есть собственные механизмы защиты;
  • думаю поднять в лаборатории ClamAV и дополнительно прогонять через него почту;
  • как фанат Proxmox, ещё смотрю в сторону Proxmox Mail Gateway как дополнительного внешнего слоя.

В итоге я бы не говорил, что «самохостинг почты небезопасен». Правильнее сказать, что это публичный сервис, который требует нормально построенной архитектуры и регулярного обслуживания.

И, как показывает практика, в домашней лаборатории при наличии ровных рук вопрос безопасности — далеко не самая большая проблема. Гораздо интереснее вопрос доступности: резервирование, репликация, отказоустойчивость, резервный MX, восстановление после отказа узла и т. п.

И ещё один важный момент: безопасность и deliverability — совершенно разные вещи. Даже идеально защищённый сервер может иметь проблемы с доставкой в Gmail/Outlook из-за репутации IP, reverse DNS и прочих факторов. Для домашнего self-hosted mail это, на мой взгляд, гораздо более реальная эксплуатационная проблема, чем «SMTP слишком небезопасен».

В последнейверсии Stalwart функциональность существенно выросла, поэтому часть “традиционных” причин ставить PMG перед ним уже может отсутствовать - надо проверять и сравнивать конечно..

В итоге решил заморочиться и повесить на один домен stalwart и bulmarkmail

Допустим, вешаем его на /webmail

  1. Качаем и собираем докер образ
git clone https://github.com/bulwarkmail/webmail.git
cd webmail
docker build --build-arg NEXT_PUBLIC_BASE_PATH=/webmail -t bulwarkmail .
  1. Запускаем в docker compose
bulwark:
    image: bulwarkmail:latest
    container_name: bulwark
    ports:
      - "3000:3000"
    environment:
      JMAP_SERVER_URL: https://mail.domain.com
      NEXT_PUBLIC_LOCALE_PREFIX: always
      TZ: Europe/Moscow
      SETTINGS_SYNC_ENABLED: "true"
  1. В моем случае в traefik надо прописать простое правило
---
rule: Host(`webmail.domain.com`)
entryPoints:
  - websecure
service: webmail-service
---
loadBalancer:
  servers:
    - url: http://stalwart.dmz.lan:3000

  1. Финт ушами в stalwart в Settings -> HTTP -> General

И теперь получаем такую схему:

  1. Можно stalwart добавить в качестве веб приложения в bulmark

А еще в маркетплейсе плагинов bulmarkmail нашел spamscore


Где видно, что при наличии верно заполненных технических валидаций stalwart успешно определяет спам