Introduction
Modern development teams run their applications inside Docker containers. Whether you are building a monolith, a collection of microservices, or a simple staging environment, email testing remains a pain point. The traditional approach is to spin up a local mail catcher such as MailHog or Mailpit as yet another container in your docker-compose.yml. That works on one developer's laptop, but the moment a second developer clones the repo, a CI pipeline runs, or a staging server is provisioned, every environment needs its own mail catcher configured from scratch.
SendPit eliminates that overhead. Because SendPit is a cloud-hosted SMTP sandbox, you do not need an extra container. Every environment -- local Docker, CI runner, staging cluster -- points at the same smtp.sendpit.com endpoint. Emails land in a shared, web-based inbox that the whole team can access. No container to maintain, no ports to expose, no volumes to persist.
This guide walks you through configuring Docker Compose services to use SendPit for email testing, with framework-specific examples, Docker secrets for credential security, multi-service patterns, and CI/CD tips.
Prerequisites
Before you start, make sure you have:
- Docker Engine 20.10 or later and Docker Compose v2.
- A SendPit account with at least one mailbox created. You can sign up at sendpit.com.
- Your SMTP credentials from the SendPit dashboard: host, port, username, and password.
Your SendPit SMTP settings will look like this:
| Setting | Value |
|---|---|
| Host | smtp.sendpit.com |
| Port | 587 |
| Encryption | TLS |
| Username | mb_a1b2c3d4e5f6g7h8 |
| Password | Your mailbox password |
Setting SMTP Environment Variables
The cleanest way to pass SMTP credentials to a Docker container is through environment variables. Docker Compose offers two approaches: inline environment keys and external env_file references.
Inline environment keys
services:
web:
image: myapp:latest
environment:
SMTP_HOST: smtp.sendpit.com
SMTP_PORT: "587"
SMTP_USERNAME: mb_a1b2c3d4e5f6g7h8
SMTP_PASSWORD: your-mailbox-password
SMTP_ENCRYPTION: tls
Using an env_file
Create a .env.sendpit file (and add it to .gitignore):
SMTP_HOST=smtp.sendpit.com
SMTP_PORT=587
SMTP_USERNAME=mb_a1b2c3d4e5f6g7h8
SMTP_PASSWORD=your-mailbox-password
SMTP_ENCRYPTION=tls
Then reference it in your compose file:
services:
web:
image: myapp:latest
env_file:
- .env.sendpit
The env_file approach keeps credentials out of your docker-compose.yml, making it safer to commit the compose file to version control.
Example docker-compose.yml
Here is a complete, minimal compose file for a web application that sends email through SendPit:
version: "3.8"
services:
web:
build: .
ports:
- "8000:8000"
env_file:
- .env.sendpit
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Notice there is no mail catcher service. Your application connects to smtp.sendpit.com over the internet, so you only need the services your application actually requires.
Framework-Specific Docker Examples
Laravel with Docker
Laravel reads mail settings from its .env file. You can either mount the .env file into the container or pass environment variables that override Laravel's config.
Option A: Pass environment variables directly
services:
php:
build: .
environment:
MAIL_MAILER: smtp
MAIL_HOST: smtp.sendpit.com
MAIL_PORT: "587"
MAIL_USERNAME: mb_a1b2c3d4e5f6g7h8
MAIL_PASSWORD: your-mailbox-password
MAIL_ENCRYPTION: tls
MAIL_FROM_ADDRESS: dev@yourapp.com
MAIL_FROM_NAME: "My App"
Option B: Mount a .env file
services:
php:
build: .
volumes:
- ./.env:/var/www/html/.env:ro
With either approach, Laravel's config/mail.php will pick up the SendPit credentials automatically. No code changes needed.
Django with Docker
Django reads SMTP settings from settings.py. Configure it to pull from environment variables:
# settings.py
import os
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('SMTP_HOST', 'smtp.sendpit.com')
EMAIL_PORT = int(os.environ.get('SMTP_PORT', 587))
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.environ.get('SMTP_USERNAME', '')
EMAIL_HOST_PASSWORD = os.environ.get('SMTP_PASSWORD', '')
Then pass the variables in your compose file:
services:
django:
build: .
env_file:
- .env.sendpit
command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
Node.js with Docker
Using Nodemailer, configure the transport from environment variables:
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST || 'smtp.sendpit.com',
port: parseInt(process.env.SMTP_PORT, 10) || 587,
secure: false,
auth: {
user: process.env.SMTP_USERNAME,
pass: process.env.SMTP_PASSWORD,
},
});
And the compose service:
services:
node:
build: .
env_file:
- .env.sendpit
ports:
- "3000:3000"
Docker Secrets for SMTP Credentials
For production-like Docker Swarm deployments or any environment where you want stronger credential isolation, Docker secrets are a better choice than plain environment variables. Secrets are mounted as files inside the container rather than being visible in docker inspect output.
Create the secrets:
echo "mb_a1b2c3d4e5f6g7h8" | docker secret create smtp_username -
echo "your-mailbox-password" | docker secret create smtp_password -
Reference them in your compose file:
version: "3.8"
services:
web:
image: myapp:latest
environment:
SMTP_HOST: smtp.sendpit.com
SMTP_PORT: "587"
SMTP_ENCRYPTION: tls
secrets:
- smtp_username
- smtp_password
secrets:
smtp_username:
external: true
smtp_password:
external: true
Inside your application, read /run/secrets/smtp_username and /run/secrets/smtp_password as files:
# The secrets are available at these paths inside the container:
/run/secrets/smtp_username
/run/secrets/smtp_password
For local development without Swarm, you can use file-based secrets:
secrets:
smtp_username:
file: ./secrets/smtp_username.txt
smtp_password:
file: ./secrets/smtp_password.txt
Multi-Service Setups
Real-world applications often have multiple services that send email: a web server for transactional emails, a background worker for queued notifications, and a cron service for scheduled reports. All of them need the same SMTP configuration.
Use YAML anchors or an env_file shared across services:
services:
web:
build: .
env_file:
- .env.sendpit
ports:
- "8000:8000"
worker:
build: .
env_file:
- .env.sendpit
command: php artisan queue:work
cron:
build: .
env_file:
- .env.sendpit
command: supercronic /etc/crontab
Every service reads the same .env.sendpit file, so credentials are defined once and shared everywhere. When you rotate your SendPit mailbox password, update the single file and restart the containers.
Docker Networking
Unlike local mail catchers that require a shared Docker network between your application container and the mail catcher container, SendPit requires no special networking configuration. Your containers reach smtp.sendpit.com over the public internet, just like they reach any other external service.
This means:
- No custom Docker networks for email.
- No
linksordepends_onentries for a mail service. - No internal DNS aliases to manage.
- Works identically in Docker Compose, Docker Swarm, and Kubernetes.
The only requirement is that your container has outbound internet access on port 587, which is the default for most Docker setups.
CI/CD with Docker
SendPit shines in CI/CD pipelines where spinning up a mail catcher adds complexity and build time. Instead, pass SendPit credentials as CI secrets and let your Dockerized tests send email to the real sandbox.
GitHub Actions
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
env:
SMTP_HOST: smtp.sendpit.com
SMTP_PORT: 587
SMTP_USERNAME: ${{ secrets.SENDPIT_USERNAME }}
SMTP_PASSWORD: ${{ secrets.SENDPIT_PASSWORD }}
run: docker compose -f docker-compose.test.yml up --abort-on-container-exit
GitLab CI
test:
stage: test
image: docker:24
services:
- docker:24-dind
variables:
SMTP_HOST: smtp.sendpit.com
SMTP_PORT: "587"
SMTP_USERNAME: $SENDPIT_USERNAME
SMTP_PASSWORD: $SENDPIT_PASSWORD
script:
- docker compose -f docker-compose.test.yml up --abort-on-container-exit
No need to add a mailhog or mailpit service to your CI pipeline. Every test run uses the same shared SendPit inbox, so you can inspect test emails from any browser.
Kubernetes
If your team has moved beyond Docker Compose to Kubernetes, the same principle applies. Store SendPit credentials in a Kubernetes Secret and reference them in your pod spec:
apiVersion: v1
kind: Secret
metadata:
name: sendpit-smtp
type: Opaque
stringData:
SMTP_HOST: smtp.sendpit.com
SMTP_PORT: "587"
SMTP_USERNAME: mb_a1b2c3d4e5f6g7h8
SMTP_PASSWORD: your-mailbox-password
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
template:
spec:
containers:
- name: web
image: myapp:latest
envFrom:
- secretRef:
name: sendpit-smtp
ConfigMaps work for non-sensitive values like SMTP_HOST and SMTP_PORT, while Secrets handle the username and password.
Troubleshooting
DNS resolution failures
If your container cannot resolve smtp.sendpit.com, check that it has a working DNS configuration. By default, Docker uses the host's DNS. You can verify with:
docker compose exec web nslookup smtp.sendpit.com
If resolution fails, try adding a public DNS server to your compose file:
services:
web:
dns:
- 8.8.8.8
- 1.1.1.1
Firewall and port issues
Some corporate networks or cloud providers block outbound traffic on port 587. Test connectivity from inside your container:
docker compose exec web nc -zv smtp.sendpit.com 587
If the connection times out, check your firewall rules or ask your network administrator to allow outbound TCP on port 587.
Connection timeouts
If connections hang, it is usually a DNS or firewall issue. Increase the SMTP timeout in your application to rule out slow networks:
- Laravel: Set
MAIL_TIMEOUT=30in your environment. - Django: Add
EMAIL_TIMEOUT = 30insettings.py. - Node.js: Pass
connectionTimeout: 30000to Nodemailer'screateTransport.
Authentication errors
Double-check that your SMTP_USERNAME starts with mb_ and matches a mailbox in your SendPit dashboard. Ensure the password has no trailing whitespace, which can happen when copying from a web interface.
SendPit vs Local Mail Catchers
Tools like MailHog and Mailpit are popular for local email testing, but they come with friction in Docker workflows:
| Concern | Local Mail Catcher | SendPit |
|---|---|---|
| Extra container needed | Yes | No |
| Shared across team | No (each dev runs their own) | Yes (shared cloud inbox) |
| Works in CI/CD | Requires service setup | Just set environment variables |
| Persistent between restarts | Only with volumes | Always persistent |
| Web UI access | localhost only | Accessible from anywhere |
| Docker network config | Required (internal networking) | Not required (external service) |
| Setup time | Configure per environment | One-time mailbox creation |
For teams that want a single, consistent email testing experience across every environment, SendPit is the simpler choice. You remove a container from your stack, eliminate per-developer setup, and gain a shared inbox that works everywhere -- from docker compose up on a laptop to a Kubernetes staging cluster.
Next Steps
- Create a mailbox in the SendPit dashboard if you have not already.
- Update your docker-compose.yml with the
env_fileapproach shown above. - Send a test email from your Dockerized application and verify it appears in the SendPit inbox.
- Read the SMTP Integration Guide for framework-specific examples beyond Docker.
- Invite your team so everyone can view captured emails in the shared inbox.