MailDev ist ein lokaler SMTP-Server mit Web-UI auf Port 1080, der jede E-Mail abfängt, statt sie zuzustellen. In 30 Sekunden via Docker gestartet, killt er den ganzen Pain mit produktiven SMTP-Servern in der Entwicklung: kein Spam an echte User, kein Deliverability-Drama, kein Warten auf Postmaster-Tools.

Das Problem mit echten SMTP-Servern in dev 😤

Wer schonmal versucht hat, Transaktionsmails gegen einen echten SMTP-Server zu entwickeln, kennt das: lokale IP nicht im SPF, DKIM fehlt, die Mail landet im Spam oder wird direkt gebounct. Wenn sie ankommt, dann nur bei den eigenen Test-Adressen, und versehentlich auch mal beim echten Kunden, weil eine Seed-Adresse durchgerutscht ist.

Dazu kommen Rate-Limits, IP-Reputation, Greylisting, TLS-Stolperfallen, und das alles für eine Mail, die du nur sehen willst, um zu prüfen, ob dein Template-Renderer keinen Mist baut. Das ist kein Dev-Workflow, das ist Selbstbestrafung.

DKIM, DMARC und SPF: Der Schutzschild deiner E-Mail-Kommunikation 🛡️
Warum echte SMTP-Setups so empfindlich sind: SPF, DKIM und DMARC erklärt.

Was MailDev macht 📬

MailDev ist ein winziges Node-Tool, das zwei Dinge gleichzeitig startet: einen SMTP-Server auf Port 1025, der jede eingehende Mail annimmt, und ein Web-UI auf Port 1080, in dem du jede Mail mit HTML-View, Plain-Text-View, Headern und Anhängen ansehen kannst. Neue Mails ploppen per WebSocket live rein, du brauchst nicht zu refreshen.

MailDev — Simple SMTP server with web UI
Offizielle Doku mit CLI-Optionen, ENV-Variablen und REST-API.
  • SMTP auf Port 1025, Web-UI auf Port 1080 (frei konfigurierbar)
  • HTML-, Text- und Header-Ansicht plus Anhänge
  • REST-API zum Auslesen und Löschen von Mails (perfekt für E2E-Tests)
  • Optionales Relay an einen echten Upstream-SMTP, wenn du doch mal raus willst
  • WebSocket-Live-Updates, kein Polling nötig
  • Optional Basic-Auth fürs Web-UI, optional HTTPS

In 30 Sekunden gestartet 🚀

Schnellste Variante via Docker, ohne npm-Install im Projekt:

docker run -p 1080:1080 -p 1025:1025 maildev/maildev:2.2.1

Öffnet http://localhost:1080, und ab dann kannst du gegen localhost:1025 senden. Wer lieber nichts via Docker macht, installiert es als npm-Paket:

npm install -g maildev
maildev

Docker Compose für dein Dev-Setup 🐳

Im echten Dev-Workflow läuft MailDev am sinnvollsten als eigener Service neben deiner API. Dann musst du nichts global installieren, und dein Team hat exakt dieselbe Mail-Sandbox, indem es docker compose up tippt.

services:
  maildev:
    image: maildev/maildev:2.2.1
    container_name: maildev
    restart: unless-stopped
    ports:
      - "1025:1025"   # SMTP
      - "1080:1080"   # Web UI
    environment:
      - MAILDEV_INCOMING_USER=
      - MAILDEV_INCOMING_PASS=
      - TZ=Europe/Berlin

  api:
    build: ./api
    environment:
      SMTP_HOST: maildev
      SMTP_PORT: 1025
      SMTP_FROM: "Dev API <[email protected]>"
    depends_on: [maildev]
Docker: Einfache Bereitstellung von Diensten 🚢
Warum sich der Aufwand lohnt: Docker als Standard-Tooling für Dev-Stacks.

Beispiel: Node.js + Nodemailer 📨

Der Klassiker. Nodemailer kennt MailDev nicht extra, weil es einfach ein normaler SMTP-Server ist. Du zeigst es per Hostname auf den MailDev-Container, deaktivierst TLS für lokale Entwicklung und gut.

import nodemailer from "nodemailer";

const transporter = nodemailer.createTransport({
  host: "localhost",
  port: 1025,
  secure: false,        // MailDev macht kein TLS in dev
  ignoreTLS: true,
  // auth bewusst weglassen, MailDev nimmt alles
});

await transporter.sendMail({
  from: '"Dev Bot" <[email protected]>',
  to: "[email protected]",
  subject: "Hallo aus dev",
  html: "<h1>Funktioniert</h1><p>Schau in MailDev :)</p>",
});
Nodemailer
Der De-facto-SMTP-Client für Node.js mit Templates, Attachments und Streams.
NPM: Der Paketmanager für dein Projekt 🪄
Falls du Nodemailer noch nie installiert hast: npm-Basics.

Beispiel: NestJS Mailer-Modul 🪺

In einer Nest-App nimmst du am besten das offizielle @nestjs-modules/mailer-Modul und zeigst es per ENV auf MailDev. Im Test/Dev läuft alles gegen MailDev, in Prod wird SMTP_HOST einfach auf den echten Server gesetzt, kein Code-Change.

// app.module.ts
import { MailerModule } from "@nestjs-modules/mailer";

@Module({
  imports: [
    MailerModule.forRoot({
      transport: {
        host: process.env.SMTP_HOST ?? "localhost",
        port: Number(process.env.SMTP_PORT ?? 1025),
        secure: false,
        ignoreTLS: true,
      },
      defaults: { from: '"NestJS Dev" <[email protected]>' },
    }),
  ],
})
export class AppModule {}
NestJS: Server-Framework auf Steroide 🎉
Warum NestJS für Backend-APIs eine richtig gute Wahl ist.
@nestjs-modules/mailer
Mailer-Modul für NestJS mit Template-Engines (Handlebars, Pug, EJS).

Beispiel: Nuxt 3 Server-Route ⚡

Nuxt 3 bringt mit Nitro einen vollwertigen Server-Layer mit. Eine API-Route, die eine Mail verschickt, ist drei Zeilen Code, und dank MailDev kannst du das End-to-End testen, ohne dass je eine Mail das Notebook verlässt.

// server/api/welcome.post.ts
import nodemailer from "nodemailer";

export default defineEventHandler(async (event) => {
  const body = await readBody<{ email: string }>(event);

  const t = nodemailer.createTransport({
    host: process.env.SMTP_HOST ?? "localhost",
    port: Number(process.env.SMTP_PORT ?? 1025),
    secure: false,
    ignoreTLS: true,
  });

  await t.sendMail({
    from: '"Nuxt" <[email protected]>',
    to: body.email,
    subject: "Welcome",
    html: "<p>Schön, dass du da bist.</p>",
  });

  return { ok: true };
});
Vue.js: Moderner Ansatz für die Frontend-Entwicklung 🚀
Vue als Grundlage: kurzer Reminder, warum Vue+Nuxt so angenehm sind.
Nitro
Der Server-Layer hinter Nuxt 3, perfekt für API-Endpunkte mit MailDev.

Alternativen: Mailpit und Mailhog ⚖️

MailDev ist nicht das einzige Tool in der Kategorie. Es gibt zwei prominente Alternativen, und es lohnt sich, sie kurz zu kennen:

  • Mailpit (Go, Nachfolger von Mailhog im Geiste): aktiv gepflegt, hat zusätzlich Spam-Score, Link-Check und Chaos-Modus für Bounce-Simulation. Wenn du "more batteries included" willst, ist Mailpit eine starke Option.
  • Mailhog (Go, älter): seit Jahren im Wartungsmodus, viele Tutorials zeigen es noch. Funktioniert, aber neue Setups würde ich nicht mehr darauf bauen.
  • MailDev (Node): minimal, super stabil, fühlt sich für Node-Projekte am natürlichsten an. Wer ohnehin schon einen Node-Stack hat, hat hier den geringsten Reibungsverlust.
axllent/mailpit
Modern alternative to MailHog, written in Go.
mailhog/MailHog
Web and API based SMTP testing.

In den Dev-Workflow einbinden 🔄

Mein Setup pro Projekt sieht so aus:

  • MailDev läuft als Service in docker-compose.yml, immer mit fixem Tag (kein :latest).
  • Alle SMTP-Configs der App lesen aus ENV (SMTP_HOST, SMTP_PORT, SMTP_FROM). Dev zeigt auf maildev, Prod auf den echten Server.
  • E2E-Tests rufen am Anfang die REST-API DELETE /email/all, danach lösen sie die Aktion aus und holen die Mail per GET /email zurück, um Assertions auf Subject und HTML zu fahren.
  • Im Onboarding-Doc steht nur: docker compose up, dann http://localhost:1080 öffnen. Niemand muss SMTP-Keys verteilen.
  • Die Production-SMTP-Credentials liegen ausschließlich im Secret-Manager, nie in der .env-Beispieldatei.

Damit ist die Mail-Pipeline lokal komplett deterministisch. Kein Spam-Score, kein Reputation-Risiko, kein "oh, war die letzte Mail jetzt von der lokalen oder der Staging-Umgebung".

Fazit 🚀

MailDev ist eines dieser Tools, die du einmal einrichtest und dann nie wieder anfasst, weil sie einfach laufen. Ein Container, zwei Ports, alle Mails deiner App sicher abgefangen, und ein Web-UI, das du auch um drei Uhr nachts noch bedienen kannst. Für jeden Stack, der irgendwo Mails verschickt, sollte das die Default-Antwort auf "wie testen wir das lokal?" sein.

Spar dir den Pain mit echten SMTP-Servern, bis du wirklich in Production deployst. Bis dahin: docker compose up.

maildev/maildev
GitHub-Repo mit REST-API-Doku, CLI-Reference und Release-Notes (MIT, v2.2.1).