Песочнице SMTP место в том же compose-файле, что и приложение: она поднимается вместе с docker compose up, не требует аккаунта, работает без интернета и — самое полезное — та же конфигурация работает в CI, где никто не будет кликать по дашборду. Контейнер, который туда ставят, обычно Mailpit: один образ, два порта, активная поддержка, и он заменил заброшенный MailHog.
Сервис
Документация Mailpit рекомендует такую форму, и именно к ней приходят почти все compose-файлы:
services:
mailpit:
image: axllent/mailpit
container_name: mailpit
restart: unless-stopped
volumes:
- ./data:/data
ports:
- 8025:8025 # веб-интерфейс
- 1025:1025 # SMTP
environment:
MP_MAX_MESSAGES: 5000
MP_DATABASE: /data/mailpit.db
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH_ALLOW_INSECURE: 1
Что даёт каждая строка:
MP_DATABASE+ том сохраняют перехваченные письма между перезапусками; без них Mailpit живёт в памяти и перезапуск контейнера очищает инбокс.MP_MAX_MESSAGESограничивает хранилище, чтобы оно не росло вечно (5000 — с запасом для разработки; в CI можно снизить до нескольких сотен).MP_SMTP_AUTH_ACCEPT_ANY/MP_SMTP_AUTH_ALLOW_INSECUREпринимают любые SMTP-доступы: многие конфигурации требуют логин и пароль даже в разработке.
Если SMTP нужен только контейнерам внутри сети, уберите маппинг 1025:1025: опубликованные порты — ровно то, из-за чего тестовый SMTP однажды становится открытым релеем в интернете.
Направьте приложение на него
Внутри compose-сети хост — это имя сервиса, а не localhost. Это самая частая ошибка с контейнерными песочницами:
# Laravel (.env)
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=anything
MAIL_PASSWORD=anything
MAIL_ENCRYPTION=null
# Django settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'mailpit'
EMAIL_PORT = 1025
EMAIL_HOST_USER = 'anything'
EMAIL_HOST_PASSWORD = 'anything'
EMAIL_USE_TLS = False
С ноутбука (вне сети) тот же сервис доступен на 127.0.0.1:1025, а веб-интерфейс — на http://127.0.0.1:8025.
Кто ещё встаёт на это место
- smtp4dev — аналог на .NET: та же идея, те же два порта, свой образ. Берите, если команда живёт в Visual Studio.
- MailHog встречается в старых compose-файлах. Он больше не поддерживается — путь миграции за пять минут описан в Альтернативе MailHog (порты совпадают, меняется только имя образа).
- Хостируемая песочница (этот инстанс) — вариант, когда контейнера не хочется вообще: compose-сервиса нет, но и «поднимать» нечего.
Как дождаться его в CI
docker compose up -d возвращает управление раньше, чем SMTP начнёт принимать соединения, — отсюда «мигающие» тесты. Либо ждите порт, либо задайте healthcheck:
healthcheck:
test: ["CMD", "/mailpit", "readyz"]
interval: 2s
timeout: 2s
retries: 15
Тогда docker compose up -d --wait (Compose v2.17+) заблокируется до готовности — без sleep в пайплайне.
Три детали, которые стоит знать
- Размер письма. Песочница, принимающая что угодно, всё равно имеет лимиты: если тест с вложением падает, сверьте лимит приложения и песочницы, прежде чем винить код.
- Таймауты. В разработке таймаут мейлера лучше держать коротким (5–10 секунд): сломанная песочница должна падать быстро, а не подвешивать запрос на минуту.
- Доступ с другого хоста. Если ловушка живёт на сервере (например, общий staging), не публикуйте 1025 наружу — привяжите к локальному интерфейсу или оставьте внутри сети: публичная ловушка — это сервис с чужой почтой внутри.
Что дальше
- Как тестировать отправку писем, ничего не отправляя — четыре шага вокруг контейнера: инбокс, конфиг приложения, просмотр, проверки в CI.
- Mailpit против MailHog и Mailtrap — если ещё выбираете между ними.
- Как ловить вебхуки локально без ngrok — вторая половина отладки.
- Альтернативы Mailtrap — более широкое сравнение.