MAATRIX / Блог / Mailtrain на сервере: частые ошибки и решения

Mailtrain на сервере: частые ошибки и решения

MAATRIX

Mailtrain — удобная self-hosted платформа для email-рассылок с сегментацией подписчиков, но именно из-за того, что это связка из нескольких сервисов (Node.js-приложение, MySQL, Redis и встроенный MTA), на боевом сервере она ломается предсказуемо в одних и тех же местах. Ниже — реальные симптомы, с которыми сталкиваются при разворачивании Mailtrain на VPS, и конкретные шаги, которые возвращают систему в рабочее состояние.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Как устроен Mailtrain и почему ошибки повторяются

Mailtrain — не монолит. Под капотом:

  • Node.js-приложение — веб-интерфейс, API, обработка шаблонов и кампаний;
  • MySQL/MariaDB — хранилище списков, подписчиков, кампаний, статистики;
  • Redis — очереди задач, сессии, кэш;
  • встроенный MTA (Zone-MTA) или внешний SMTP-релей — фактическая отправка писем.

Почти все проблемы сводятся к разрыву связи между этими компонентами: приложение не достучалось до базы, очередь в Redis не разбирается, MTA не может достучаться до принимающего сервера. Держите это в голове — тогда диагностика превращается в проверку конкретного звена, а не в гадание.

Типовой docker-compose.yml для self-hosted установки выглядит так:

version: "3"
services:
  mailtrain:
    image: mailtrain/mailtrain:latest
    restart: always
    ports:
      - "3000:3000"
      - "3003:3003"
      - "3004:3004"
    env_file:
      - docker-mailtrain.env
    depends_on:
      - mysql
      - redis
    volumes:
      - mailtrain-files:/app/server/files
      - mailtrain-certs:/certs

  mysql:
    image: mysql:8.0
    restart: always
    command: --default-authentication-plugin=mysql_native_password
    environment:
      - MYSQL_ROOT_PASSWORD=change_me
      - MYSQL_DATABASE=mailtrain
    volumes:
      - mailtrain-mysql:/var/lib/mysql

  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - mailtrain-redis:/data

volumes:
  mailtrain-mysql:
  mailtrain-redis:
  mailtrain-files:
  mailtrain-certs:

Порт 3000 — админка (trusted), 3003/3004 — публичные и API-эндпоинты (отписка, трекинг, вебхуки). Их нужно держать в голове при настройке фаервола и reverse proxy.

Контейнеры не стартуют: MySQL и Redis не отвечают

Самая частая ошибка после первого docker compose up -d — приложение падает в рестарт-луп с логом вида:

Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'mailtrain'@'%'

или

Error: connect ECONNREFUSED 127.0.0.1:6379

Причины почти всегда одни и те же:

  1. Приложение стартовало раньше, чем MySQL успел инициализировать базу. depends_on в Docker Compose гарантирует только порядок запуска контейнеров, но не готовность сервиса внутри. Добавьте healthcheck для MySQL и condition: service_healthy в depends_on, либо просто перезапустите контейнер mailtrain после того, как MySQL поднимется (docker compose restart mailtrain).
  2. Переменные окружения в docker-mailtrain.env не совпадают с реальными — например, MYSQL_HOST=mysql (имя сервиса, не localhost), MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, REDIS_HOST=redis. Если MySQL и Redis крутятся не в Docker, а на хосте, localhost внутри контейнера означает сам контейнер, а не хост — нужен либо network_mode: host, либо реальный IP хоста.
  3. MySQL 8 по умолчанию использует caching_sha2_password, а старые клиентские библиотеки Mailtrain ждут mysql_native_password. Если видите ошибку аутентификации при явно верном пароле — смените плагин аутентификации для пользователя:
ALTER USER 'mailtrain'@'%' IDENTIFIED WITH mysql_native_password BY 'ваш_пароль';
FLUSH PRIVILEGES;

Подробнее про типовые проблемы самого MySQL — в статье MySQL на сервере: частые ошибки и решения, а про Redis — в Redis на сервере: частые ошибки и решения.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Кампании зависают в статусе «Sending»

Кампания запущена, счётчик отправленных писем не двигается или двигается очень медленно. Это почти всегда про очередь Redis или встроенный MTA:

  • Redis недоступен или переполнен — Mailtrain держит очередь отправки в Redis, и если контейнер Redis перезапустился без persistence (appendonly no и без volume), очередь могла обнулиться вместе с состоянием сессий. Проверьте redis-cli -h redis PING из контейнера mailtrain и настройте appendonly yes для сохранности данных между рестартами.
  • Встроенный Zone-MTA не может достучаться до внешних серверов — на VPS без настроенного PTR-записи (обратной DNS-зоны) многие крупные почтовики (Gmail, Outlook) просто отбрасывают соединение или держат его до таймаута. Проверьте логи MTA:
docker compose logs -f mailtrain | grep -i "smtp\|zone-mta\|delivery"

Если видите Connection timed out или 550 5.7.1 — дело не в Mailtrain, а в репутации IP-адреса и отсутствии PTR. Закажите PTR-запись у хостера или переключитесь в настройках «Send configuration» на внешний SMTP-релей (Postfix на отдельном сервере, Mailgun, Amazon SES) — это надёжнее, чем полагаться на встроенный MTA на молодом IP.

  • Диск переполнен. Zone-MTA пишет очередь на диск; если df -h показывает 100% на разделе с /app или volume mailtrain-files — отправка встанет молча, без явной ошибки в UI. Чистите старые логи кампаний и следите за местом заранее.

Если вы ещё не определились, через что слать — встроенный MTA или отдельный Postfix, — почитайте массовую рассылку с сервера: частые ошибки и решения: там разобраны компромиссы между простотой и контролем репутации.

Ссылки отписки и трекинг открытий не работают за nginx

Если Mailtrain стоит за reverse proxy (а на боевом сервере он почти всегда там стоит — ради SSL и человеческого домена), частая жалоба: ссылки в письмах ведут на localhost:3000 или трекинг-пиксель не открывается, хотя сам интерфейс работает нормально.

Причина — переменные TRUSTED_URL_BASE, SANDBOX_URL_BASE и PUBLIC_URL_BASE в конфиге Mailtrain должны указывать на реальные внешние адреса, а не на внутренние порты контейнера:

TRUSTED_URL_BASE=https://mail.example.com
SANDBOX_URL_BASE=https://sandbox.mail.example.com
PUBLIC_URL_BASE=https://links.example.com
WWW_PROXY=true

WWW_PROXY=true обязателен, когда приложение работает за прокси — иначе Mailtrain не доверяет заголовкам X-Forwarded-* и генерирует ссылки на внутренний адрес. Конфиг nginx для проксирования должен пробрасывать WebSocket-заголовки — интерфейс кампаний использует WS для live-обновления статуса отправки:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Без Upgrade/Connection заголовков интерфейс будет работать, но статус кампаний и live-статистика зависнут — придётся вручную обновлять страницу.

Письма уходят в спам, несмотря на «зелёный» статус отправки

Mailtrain честно отчитается «доставлено», но письмо может лечь в папку «Спам» получателя — это разные уровни ответственности. Приложение управляет очередью и шаблонами, а репутацию домена и IP оно не контролирует.

Проверочный чек-лист:

  1. SPF, DKIM, DMARC настроены и совпадают с доменом отправителя. В Mailtrain DKIM-ключ генерируется в разделе Settings → Send Configurations — публичную часть нужно вручную добавить TXT-записью в DNS. Если этого не сделать, крупные почтовики (Gmail, Mail.ru, Yandex) занижают доверие письму сразу.
  2. From-домен совпадает с доменом, для которого настроен DKIM. Частая ошибка — слать от noreply@site.ru, а DKIM-ключ выпустить для mail.site.ru. Подписи не совпадут — DKIM просто не пройдёт проверку.
  3. IP «прогрет». Новый VPS с чистым IP, с которого сразу шлют 10 000 писем, почти гарантированно попадёт под ограничения приёмных серверов. Начинайте с малых объёмов (несколько сотен писем в день) и наращивайте постепенно.
  4. В письмах нет ссылок на URL-сокращатели и подозрительных вложений — спам-фильтры реагируют на них жёстко вне зависимости от репутации отправителя.

Подробный разбор настройки самой связки SPF/DKIM/DMARC и типичных опечаток в TXT-записях — в статье SPF, DKIM и DMARC на сервере: частые ошибки и решения.

Тормозит на больших списках подписчиков

С ростом базы (десятки и сотни тысяч подписчиков) вылезают проблемы, которых не было на тесте с сотней тестовых адресов:

  • Импорт CSV зависает или падает по таймауту. По умолчанию MySQL ограничивает размер пакета через max_allowed_packet — при импорте больших списков с длинными кастомными полями это может обрубать транзакцию. Увеличьте параметр в конфиге MySQL:
[mysqld]
max_allowed_packet=256M

Импортируйте списки частями по 20–50 тысяч записей, а не одним файлом на полмиллиона строк — так проще локализовать проблемную строку, если она есть.

  • Node.js-процесс упирается в память. На VPS с 1 ГБ RAM формирование кампании на большой список (рендер персонализированных шаблонов для каждого получателя) легко приводит к OOM-килу процесса. Ориентируйтесь минимум на 2 ГБ RAM для базы в десятки тысяч подписчиков, и обязательно настройте swap — Node не всегда изящно обрабатывает нехватку памяти, чаще просто падает.
  • Redis становится узким местом при параллельных кампаниях. Если запускаете несколько крупных рассылок одновременно, очередь в Redis растёт быстрее, чем разбирается MTA. Здесь помогает либо ограничение параллелизма кампаний в настройках, либо вынос Redis на отдельный процесс/сервер с достаточным объёмом памяти под очередь.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

Можно ли использовать Mailtrain без встроенного MTA?

Да, и для боевой рассылки это часто разумнее. В Settings → Send Configurations добавьте внешний SMTP (Postfix на отдельном сервере, Mailgun, Amazon SES) — так вы получите отдельную репутацию IP под рассылки и не зависите от встроенного Zone-MTA.

Почему после перезапуска контейнеров все пользователи разлогинены?

Сессии Mailtrain хранятся в Redis. Если Redis-контейнер поднимается без persistent volume, при рестарте данные сессий теряются. Добавьте volume и appendonly yes в конфиг Redis.

Как понять, что письма реально не доходят, а не просто задерживаются?

Смотрите логи MTA на строки с кодами 550, 553, 421 — это отказ или временная блокировка от принимающего сервера. Коды 4xx означают, что стоит повторить позже (временная проблема, например превышение лимита), 5xx — постоянный отказ, письмо не дойдёт при повторной попытке без изменения условий.

Нужен ли отдельный сервер под Mailtrain, или хватит той же VPS, что и под сайт?

Для небольших объёмов (до нескольких тысяч писем в день) хватает одной VPS с 2 ГБ RAM. При росте базы и частоты рассылок лучше вынести Mailtrain на отдельный сервер — так пиковая нагрузка на отправку не будет задевать основной сайт, а проблемы с репутацией IP не затронут остальные сервисы.

Что делать, если ссылка отписки ведёт на ошибку 404?

Проверьте PUBLIC_URL_BASE в конфиге — если она не совпадает с реальным доменом, который проксирует nginx, ссылки в уже отправленных письмах будут битыми до следующей рассылки (перегенерировать старые письма нельзя, только поправить конфиг для новых кампаний).

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →