When developing applications that send emails, having a reliable way to test those emails without actually sending them to real recipients is crucial. While services like Mailtrap offer excellent solutions for this, sometimes you need a lightweight, self-hosted option that’s quick to set up and works entirely offline. Enter Mailhog.
Mailhog is an open-source email testing tool that acts as an SMTP server and a web UI to view intercepted emails. It’s incredibly easy to use, making it a fantastic alternative for local development, especially when an internet connection isn’t available or you prefer to keep everything within your local environment.
The easiest and most recommended way to run Mailhog is via Docker. This ensures a consistent environment and prevents conflicts with other tools on your system.
In this guide, we’ll walk through setting up Mailhog using Docker and demonstrate how to configure popular frameworks like Laravel (PHP), Next.js (Node.js), and Django (Python) to send emails to Mailhog.
Getting Started with Mailhog via Docker
First, ensure you have Docker Desktop (or Docker Engine and Docker Compose) installed on your system.
To run Mailhog, simply execute the following Docker command:
docker run -d --name docker-mailhog -p 1025:1025 -p 8025:8025 mailhog/mailhog
Let’s break down this command:
-
-d
: Runs the container in detached mode (in the background). -
--name mailhog
: Assigns the namemailhog
to your container, making it easier to manage. -
-p 1025:1025
: Maps port1025
on your host machine to port1025
inside the container. This is Mailhog’s SMTP port. -
-p 8025:8025
: Maps port8025
on your host machine to port8025
inside the container. This is Mailhog’s web UI port. -
mailhog/mailhog
: Specifies the Docker image to pull and run.
If you are on a windows machine with Docker desktop, you should able to see this running on your docker desktop panel
After running the command, open your web browser and navigate to http://localhost:8025
to access the Mailhog dashboard. This is where you’ll see all your intercepted emails.
To stop Mailhog:
docker stop mailhog
To remove the container (after stopping):
docker rm mailhog
Now, let’s configure our applications.
1. Laravel Project (PHP)
Laravel makes configuring email very straightforward. We’ll adjust the .env
file to point to our Mailhog instance running in Docker.
Configuration:
Open your Laravel project’s .env
file and set the following:
MAIL_MAILER=smtp
MAIL_HOST=localhost # Or the IP of your Docker host if not localhost
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
Important Note: If you are running your Laravel application inside Docker (e.g., via Docker Compose), the MAIL_HOST
should typically be the service name of your Mailhog container (e.g., mailhog
). However, for simplicity and assuming your Laravel app is running directly on your host machine, localhost
is correct for connecting to the Mailhog container’s exposed port.
Sending an Email:
You can quickly test sending an email using a Tinkerwell console or by creating a simple route.
-
Using Tinkerwell (or
php artisan tinker
):php artisan tinker
Inside tinker, run:
Mail::raw('This is a test email from Laravel.', function ($message) { $message->to('test@example.com') ->subject('Laravel Mailhog Test'); });
You should see
=> null
if successful. -
Using a Route:
Add the following to your
routes/web.php
:use IlluminateSupportFacadesMail; use IlluminateSupportFacadesRoute; Route::get('/send-mail-laravel', function () { Mail::raw('This is a test email from Laravel via a route.', function ($message) { $message->to('route-test@example.com') ->subject('Laravel Route Mailhog Test'); }); return "Email sent to Mailhog from Laravel!"; });
Visit
http://localhost:8000/send-mail-laravel
(assuming your Laravel app is running on port 8000).
Verification:
After sending the email, open your Mailhog UI at http://localhost:8025
. You should see the incoming email(s) in the inbox.
2. Node.js with Next.js
For Node.js applications, Nodemailer
is the go-to library for sending emails. We’ll set up a simple API route in Next.js to send an email to Mailhog.
Installation:
First, install Nodemailer:
npm install nodemailer
# or
yarn add nodemailer
Configuration and Sending Email (Next.js API Route):
Create an API route at pages/api/send-mail.js
:
import nodemailer from "nodemailer";
export default async function handler(req, res) {
if (req.method === "POST") {
try {
// Create a Nodemailer transporter using Mailhog's SMTP settings
let transporter = nodemailer.createTransport({
host: "localhost", // Mailhog Docker container's exposed port
port: 1025,
secure: false, // Mailhog runs without SSL/TLS by default
ignoreTLS: true, // Also ignore TLS for simple local setup
});
// Send mail with defined transport object
let info = await transporter.sendMail({
from: '"Next.js Mailhog Test" ', // sender address
to: "recipient@example.com", // list of receivers
subject: "Next.js Mailhog Test Subject", // Subject line
text: "Hello from Next.js with Mailhog!", // plain text body
html: "Hello from Next.js with Mailhog!", // html body
});
console.log("Message sent: %s", info.messageId);
res.status(200).json({ message: "Email sent successfully!", info });
} catch (error) {
console.error("Error sending email:", error);
res.status(500).json({ error: "Failed to send email." });
}
} else {
res.setHeader("Allow", ["POST"]);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
CLI Verification:
To trigger this API route and send an email, you can use curl
from your terminal. Ensure your Next.js development server is running (npm run dev
or yarn dev
).
curl -X POST http://localhost:3000/api/send-mail
You should receive a JSON response similar to:
{"message":"Email sent successfully!","info":{"accepted":["recipient@example.com"],"rejected":[],"ehlo":["PIPELINING","SIZE 31457280","ETRN","STARTTLS","AUTH PLAIN LOGIN CRAM-MD5","ENHANCEDSTATUSCODES","8BITMIME","DSN","VRFY","XACK","XTEXTC","XEXCH50"],"envelopeTime":11,"messageTime":8,"messageSize":364,"response":"250 2.0.0 Ok: queued as C12B89547","envelope":{"from":"no-reply@example.com","to":["recipient@example.com"]},"messageId":"" }}
Verification:
Head over to http://localhost:8025
and confirm that the email from your Next.js application has been received by Mailhog.
3. Python with Django
Django has robust email capabilities built-in, and configuring it to use a local SMTP server like Mailhog is straightforward.
Configuration:
Open your Django project’s settings.py
file and add or modify the following:
# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'localhost' # Mailhog Docker container's exposed port
EMAIL_PORT = 1025
EMAIL_USE_TLS = False # Mailhog typically doesn't use TLS/SSL by default
EMAIL_USE_SSL = False
# EMAIL_HOST_USER = '' # Not needed for Mailhog unless configured
# EMAIL_HOST_PASSWORD = '' # Not needed for Mailhog unless configured
DEFAULT_FROM_EMAIL = 'webmaster@localhost.com'
Sending an Email:
You can send an email from the Django shell or through a simple management command or view.
-
Using Django Shell:
python manage.py shell
Inside the shell, run:
from django.core.mail import send_mail send_mail( 'Django Mailhog Test Subject', 'Here is the message from Django.', 'from@example.com', # From address ['to@example.com'], # List of recipient email addresses fail_silently=False, )
You should see
1
as the output if one email was sent successfully. -
Using a Django Management Command (for CLI testing):
Create a new management command. In your app (e.g.,
my_app
), createmanagement/commands/send_test_email.py
:# my_app/management/commands/send_test_email.py from django.core.management.base import BaseCommand from django.core.mail import send_mail class Command(BaseCommand): help = 'Sends a test email to Mailhog' def handle(self, *args, **options): self.stdout.write("Attempting to send a test email to Mailhog...") try: send_mail( 'Django CLI Mailhog Test', 'This email was sent from a Django management command.', 'cli@example.com', ['cli-recipient@example.com'], fail_silently=False, ) self.stdout.write(self.style.SUCCESS('Email sent successfully! Check Mailhog.')) except Exception as e: self.stdout.write(self.style.ERROR(f'Failed to send email: {e}'))
CLI Verification (Django Management Command):
Run the newly created management command:
python manage.py send_test_email
You should see output similar to:
Attempting to send a test email to Mailhog...
Email sent successfully! Check Mailhog.
Verification:
Go to your Mailhog web interface at http://localhost:8025
and observe the incoming email from your Django application.
Conclusion
Using Mailhog with Docker provides a robust, isolated, and easy-to-manage solution for local email testing across various development stacks. By integrating it into your development workflow, you can confidently test email functionalities without relying on external services or accidentally spamming real users. So, the next time you’re working on an application that sends emails, remember: No Mailtrap, No Problem! – Mailhog has got you covered, especially with the power of Docker.