Mailtrain в Docker Compose: готовый файл
Если вы устали от лимитов Mailchimp или Sendpulse и хотите свою платформу рассылок с полным контролем над базой подписчиков — Mailtrain закрывает эту задачу без месячной платы за письмо. Ниже рабочий docker-compose.yml, который поднимает Mailtrain с MySQL и Redis за один docker compose up, плюс всё, что вокруг него обычно ломается: отправка почты, HTTPS, DKIM и первый запуск.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Mailtrain и зачем он вам
Mailtrain — open-source платформа email-рассылок на Node.js: списки подписчиков, сегментация по полям и поведению, кампании, шаблоны в визуальных редакторах (GrapesJS и Mosaico), статистика открытий и кликов, автоматизация (триггерные письма по событиям). По функциям это ближе к Mailchimp, чем к простому «отправителю» — отсюда и более тяжёлый стек: Node.js-приложение, MySQL/MariaDB и опционально Redis.
Сегментация в Mailtrain — не просто фильтр по тегу. Вы создаёте кастомные поля подписчика (город, дата регистрации, сумма покупок, любое число или дату) и строите сегменты на условиях по этим полям и по истории взаимодействия с прошлыми кампаниями («открыл письмо X, но не кликнул»). Для рассылок с реальной базой в 5–50 тысяч адресов и разными сценариями по сегментам это то, ради чего вообще стоит городить self-hosted, а не брать конструктор.
Если вам не нужна вся эта визуальная машинерия и хочется более лёгкий стек на Go — присмотритесь к Listmonk, он проще в эксплуатации, но и сегментация там менее гибкая. Mailtrain выигрывает там, где нужны кастомные поля, кастомные шаблоны и триггерные цепочки писем.
Требования к серверу и зависимости
Mailtrain v2 — это Node.js 14+, MySQL 8+ или MariaDB 10+, и опционально Redis для кэша сессий. Официальный образ mailtrain/mailtrain уже включает само приложение, БД и Redis разворачиваете рядом отдельными контейнерами.
По ресурсам ориентируйтесь не столько на сам Mailtrain (он лёгкий в простое), сколько на объём и частоту рассылок — MySQL под очередью кампании и генерацией статистики нагружает диск и память заметнее, чем веб-интерфейс. Общую логику подбора VPS под почтовые рассылки — сколько RAM и на что она уходит при росте базы подписчиков — я разбирал в статье про ресурсы VPS для почтовой рассылки; для Mailtrain принципы те же, с поправкой на то, что MySQL прожорливее SQLite, на котором работает часть конкурентов.
Диск: закладывайте с запасом под MySQL-дамп, загруженные изображения в шаблонах и отчёты по кампаниям — на практике база разрастается быстрее, чем кажется на старте, особенно если храните полную историю открытий/кликов.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml для Mailtrain
Рабочий файл с MariaDB, Redis и самим приложением. Пароли и секрет ниже — плейсхолдеры, замените их перед запуском на свои.
services:
mysql:
image: mariadb:10.11
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ChangeMeRootPass123
MYSQL_DATABASE: mailtrain
MYSQL_USER: mailtrain
MYSQL_PASSWORD: ChangeMeDbPass123
volumes:
- mailtrain-mysql:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- mailtrain-redis:/data
mailtrain:
image: mailtrain/mailtrain:latest
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
ports:
- "3000:3000" # trusted — админка
- "3003:3003" # sandbox — редактор шаблонов
- "3004:3004" # public — подписка/отписка, архив
environment:
WWW_SECRET: ChangeMeLongRandomSecretString
ADMIN_PASSWORD: ChangeMeAdminPassword123
MYSQL_HOST: mysql
MYSQL_DATABASE: mailtrain
MYSQL_USER: mailtrain
MYSQL_PASSWORD: ChangeMeDbPass123
REDIS_HOST: redis
REDIS_PORT: 6379
WITH_ZONE_MTA: 0
URL_BASE_TRUSTED: "http://your-server-ip:3000"
URL_BASE_SANDBOX: "http://your-server-ip:3003"
URL_BASE_PUBLIC: "http://your-server-ip:3004"
volumes:
- mailtrain-config:/app/config
- mailtrain-uploads:/app/public/grapejs/uploads
- mailtrain-reports:/app/protected/reports
volumes:
mailtrain-mysql:
mailtrain-redis:
mailtrain-config:
mailtrain-uploads:
mailtrain-reports:
Три URL_BASE — не избыточность, а архитектура Mailtrain: приложение реально поднимает три отдельных HTTP-эндпоинта на трёх портах — trusted (админка, только для вас), sandbox (изолированный рендер HTML-шаблонов, чтобы чужой JS в письме не мог достучаться до админской сессии) и public (страницы подписки/отписки и веб-архив кампаний, куда попадают подписчики). Разносить их по разным поддоменам — стандартная практика, не паранойя разработчиков.
WITH_ZONE_MTA: 0 отключает встроенный в Mailtrain MTA — об этом отдельно ниже, это осознанный выбор, а не упрощение файла.
Поднимаем:
docker compose up -d
docker compose logs -f mailtrain
Первый старт создаёт схему в MySQL — подождите, пока в логах не появится строка о готовности слушать порты, обычно это 30–60 секунд.
Настройка отправки писем: Zone-MTA или внешний SMTP-релей
У Mailtrain есть встроенный Zone-MTA — модуль, который сам отправляет письма напрямую через SMTP получателя, без промежуточного релея. Звучит удобно, но на практике это плохая идея для VPS: порт 25 наружу у большинства хостеров либо закрыт по умолчанию, либо требует отдельного запроса на разблокировку, а без готовой IP-репутации и корректного PTR письма с первого дня будут падать в спам почти у всех крупных провайдеров. Поэтому в конфиге выше WITH_ZONE_MTA=0 — Zone-MTA выключен, и Mailtrain отправляет письма через внешний SMTP-релей.
Релей настраивается не в docker-compose, а в самой админке: Configuration → Send configurations → добавляете SMTP-хост, порт, логин/пароль и режим TLS. В качестве релея подойдёт:
- ваш собственный Postfix или Mailcow на отдельном сервере с уже прогретой репутацией;
- транзакционный сервис (SendGrid, Mailgun, Amazon SES и подобные) — быстрее по деньгам на старте, но добавляет зависимость от стороннего API.
Если ещё не решили, поднимать ли собственный почтовый сервер под релей или взять готовый сервис — сравнение вариантов и то, когда каждый оправдан, я разбирал в статье Postfix или Mailcow: что выбрать для сервера. Каким бы релеем вы ни воспользовались, доставляемость всё равно упирается в SPF/DKIM/DMARC на вашем домене отправителя — а не только на сервере релея, это частая путаница. Если письма всё равно уходят в спам после настройки релея, разбор типичных причин и чек-лист исправления — в статье про попадание писем в спам.
DKIM для самого Mailtrain настраивается в Configuration → DKIM: генерируете пару ключей прямо в интерфейсе, публичный кладёте TXT-записью в DNS вашего домена отправителя, приватный остаётся в конфиге приложения. Без этого шага письма от Mailtrain будут подписываться доменом релея, а не вашим — это тоже бьёт по репутации и доверию почтовых систем.
HTTPS и reverse-proxy
Отдавать Mailtrain напрямую по HTTP на трёх портах в продакшене не стоит — минимум ради того, чтобы пароль админки не летал открытым текстом. Ставим nginx перед контейнером и заворачиваем каждый из трёх портов на свой поддомен:
server {
listen 443 ssl;
server_name mail.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
server {
listen 443 ssl;
server_name sandbox.example.com;
location / {
proxy_pass http://127.0.0.1:3003;
proxy_set_header Host $host;
}
}
server {
listen 443 ssl;
server_name public.example.com;
location / {
proxy_pass http://127.0.0.1:3004;
proxy_set_header Host $host;
}
}
SSL-сертификаты — Certbot с автопродлением, как для любого другого сайта на сервере. После настройки поддоменов обновите переменные в docker-compose: URL_BASE_TRUSTED=https://mail.example.com, URL_BASE_SANDBOX=https://sandbox.example.com, URL_BASE_PUBLIC=https://public.example.com (без указания портов), добавьте WWW_PROXY: 1, чтобы приложение доверяло заголовкам X-Forwarded-* от nginx, и уберите публикацию портов наружу в самом docker-compose (ports можно оставить только на 127.0.0.1, если nginx на том же сервере). После изменения env-переменных перезапустите контейнер: docker compose up -d --force-recreate mailtrain.
Первый запуск, безопасность и бэкапы
Логин по умолчанию — admin, пароль test, если вы не задали ADMIN_PASSWORD в переменных окружения. У Mailtrain здесь есть известная особенность: пароль администратора не просто «дефолтный при первой установке» — он реально перезаписывается значением ADMIN_PASSWORD при каждом рестарте контейнера. Смените его через переменную окружения, а не через интерфейс после первого входа — иначе следующий перезапуск контейнера откатит его обратно.
Дальше в интерфейсе:
- Создайте список (List) — базовую единицу подписчиков.
- Добавьте кастомные поля под вашу сегментацию (Fields).
- Импортируйте подписчиков CSV-файлом или через API.
- Создайте сегмент (Segments) на условиях по полям — например, «подписался позже даты X и не открыл ни одного письма».
- Соберите шаблон в GrapesJS или Mosaico, привяжите его к кампании.
Бэкап сводится к дампу MySQL-тома и папки конфига: том mailtrain-mysql содержит все списки, подписчиков и статистику, mailtrain-config — ключи DKIM и настройки релея. Регулярный mysqldump из контейнера плюс архив томов через docker run --rm -v mailtrain-config:/data -v $(pwd):/backup alpine tar czf /backup/config.tar.gz /data — рабочая минимальная схема, которую можно поставить в cron.
Обновление образа — стандартно для docker compose: docker compose pull mailtrain && docker compose up -d mailtrain. Перед мажорным обновлением делайте дамп БД — Mailtrain v2 был полным переписыванием v1 без обратной совместимости данных, и повторения такого разработчики не обещали, но осторожность дешевле восстановления базы подписчиков вручную.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли обойтись без Redis?
Да, в конфигурации есть переключатель WITH_REDIS, отключающий его — сессии и кэш тогда идут через MySQL. Для небольшой инсталляции это упрощает стек до двух контейнеров, но на активной админке с несколькими пользователями Redis снимает лишнюю нагрузку с БД.
Зачем три отдельных порта, если у конкурентов один?
Такая архитектура закладывалась разработчиками Mailtrain как изоляция: рендер чужого HTML из писем (sandbox) и публичные страницы подписки (public) физически не должны иметь доступа к сессии администратора (trusted). Неудобно настраивать, зато меньше риска, что уязвимость в рендере шаблона доберётся до админки.
Почему письма не уходят при включённом Zone-MTA?
Чаще всего порт 25 закрыт исходящим фаерволом хостера — это стандартная защита от рассылки спама с новых серверов. Проверьте telnet smtp.gmail.com 25 с сервера; если соединение не устанавливается, порт закрыт и нужен либо запрос на разблокировку у хостинга, либо переход на внешний SMTP-релей на альтернативном порту (587/465).
Как перенести Mailtrain на другой сервер?
Останавливаете контейнеры, копируете именованные тома (docker run --rm -v mailtrain-mysql:/from -v /path/to/new:/to alpine cp -a /from/. /to) на новый сервер, поднимаете тот же docker-compose.yml с теми же именами томов и переменными окружения.
Нужен ли выделенный IP под Mailtrain?
Для самого веб-интерфейса — нет, обычный shared IP с nginx по доменному имени работает нормально. Для исходящей отправки почты (если всё же используете Zone-MTA, а не релей) — да, отдельный IP с чистой репутацией и настроенным PTR обязателен.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →