DEV Community

Rafael Rodrigues
Rafael Rodrigues

Posted on

MailHog: a Free, Containerized SMTP Server for Local Development

Are you still relying on limited third‑party SMTP services to test your email features? Hosted services often impose monthly sending caps, throttle rates, and require external network access. Self‑hosting MailHog in Docker eliminates these constraints, offers complete control over your local email pipeline, and reduces exposure to external dependencies. Unlike freemium platforms such as Mailtrap or SendGrid’s free tier, MailHog runs entirely on your machine at zero cost and without registration.

Using MailHog with Docker

MailHog is distributed as a lightweight Docker image. You can isolate one container per application to prevent mixing email logs across projects. By default, MailHog stores messages in memory, so every restart clears your inbox. To persist emails across restarts, configure the MH_STORAGE and MH_MAILDIR_PATH environment variables in your Docker Compose file and mount a volume:

 mailhog:
    image: mailhog/mailhog
    container_name: mailhog
    restart: unless-stopped
    environment:
      MH_STORAGE: maildir                  # switch from in-memory to maildir on disk :contentReference[oaicite:0]{index=0}
      MH_MAILDIR_PATH: /mailhog/data       # where on the container to store the maildir
    volumes:
      - ./mailhog_data:/mailhog/data
    ports:
      - '1025:1025'  # SMTP
      - '8025:8025'  # Web UI
Enter fullscreen mode Exit fullscreen mode

This creates a mailhog_data folder at your project root. Add it to .gitignore to avoid committing email logs.

If you prefer a single Docker command instead of Compose:

docker run -d \
  --name mailhog \
  --restart unless-stopped \
  -e MH_STORAGE=maildir \
  -e MH_MAILDIR_PATH=/mailhog/data \
  -v "$(pwd)/mailhog_data:/mailhog/data" \
  -p 1025:1025 \
  -p 8025:8025 \
  mailhog/mailhog
Enter fullscreen mode Exit fullscreen mode

Access it using http://localhost:8025/

Image showing MailHog UI

Simulating Failures with Jim

MailHog includes an optional chaos‑testing feature called Jim. Jim helps you test how your application handles intermittent SMTP issues: delayed deliveries, random drops, temporary network errors, and more. With Jim enabled you can verify retry logic, error handling, and user notifications under real‑world failure scenarios.

Integrating MailHog with NestJS

Below is an example of configuring Nest’s MailerModule to use MailHog. No authentication is required by default. Adjust config.smtpMailHost and related variables to your environment variables.

import { Module } from '@nestjs/common';
import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { join } from 'path';

import { AppConfigModule } from '../config/app-config.module';
import { AppEnvConfigService } from '../config/environment-variables/app-env.config';
import { MailService } from 'src/application/mail/mail.service';

export function getTemplateDir(): string {
  const srcDir = join(
    process.cwd(),
    'src',
    'infrastructure',
    'mail',
    'templates',
  );
  const distDir = join(__dirname, 'templates');
  return process.env.NODE_ENV === 'production' ? distDir : srcDir;
}

@Module({
  imports: [
    MailerModule.forRootAsync({
      imports: [AppConfigModule],
      inject: [AppEnvConfigService],
      useFactory: (config: AppEnvConfigService) => ({
        transport: {
          host: config.smtpMailHost,
          port: config.smtpMailPort,
          secure: config.smtpMailSecure ?? false, // optional TLS toggle
          auth: config.smtpMailUser
            ? {
                // if you need auth:
                user: config.smtpMailUser,
                pass: config.smtpMailPassword,
              }
            : undefined,
        },
        defaults: {
          from: config.smtpMailFrom,
        },
        template: {
          dir: getTemplateDir(),
          adapter: new HandlebarsAdapter(),
          options: { strict: true },
        },
      }),
    }),
  ],
  providers: [MailService],
  exports: [MailService],
})
export class MailModule {}

Enter fullscreen mode Exit fullscreen mode

Testing MailHog Setup

For a complete example of health checks and automated tests for your MailHog setup, check them out here in this repository.

How about you? Have you tried MailHog or similar containerized SMTP? Share your experience and any tips in the comments!

Top comments (0)