Documentation menu

CLI & configuration

Every MailDev command-line flag, environment variable, and configuration-file key, with defaults and precedence rules.

Usage

maildev [options]
maildev init [--force] [--json]

Configuration comes from four places. Later entries win:

  1. Defaults
  2. A configuration file
  3. Environment variables
  4. Command-line flags

Scaffolding a config file

maildev init walks you through the common settings and writes a configuration file in the current directory:

maildev init
maildev init --force   # overwrite an existing file
maildev init --json    # write .maildevrc.json rather than prompting for a format

Configuration files

Searched in the current directory and then each parent, first match wins:

File Format
.maildevrc.json JSON
maildev.config.ts TypeScript
maildev.config.js CommonJS or ESM, depending on your package.json
maildev.config.mjs ES module

Point at a specific file with --config <path>.

{
  "smtp": 1025,
  "web": 1080,
  "maxEmails": 200,
  "mailDirectory": "./tmp/mail",
  "mcp": true
}

Keys are the camelCase form of the flag name: --base-pathname is basePathname, --max-emails is maxEmails.

SMTP server

Flag Environment variable Default Description
-s, --smtp <port> MAILDEV_SMTP_PORT 1025 Port the SMTP server listens on
--ip <address> MAILDEV_IP :: Address to bind the SMTP server to
--incoming-user <user> MAILDEV_INCOMING_USER Require this SMTP username
--incoming-pass <password> MAILDEV_INCOMING_PASS Require this SMTP password
--incoming-secure MAILDEV_INCOMING_SECURE off Use implicit TLS on the SMTP listener
--incoming-cert <path> TLS certificate for the SMTP listener
--incoming-key <path> TLS key for the SMTP listener
--hide-extensions <list> Comma-separated SMTP extensions not to advertise
--max-message-size <bytes> MAILDEV_MAX_MESSAGE_SIZE 52428800 Reject messages larger than this. 0 disables the limit

--hide-extensions accepts STARTTLS, PIPELINING, 8BITMIME, and SMTPUTF8. Hiding STARTTLS is the usual reason to reach for it — some clients upgrade opportunistically and then fail, and not advertising the extension is easier than fixing the client.

The default bind address :: accepts both IPv6 and IPv4 connections on most systems. If a client resolves localhost to ::1 and cannot connect, bind explicitly with --ip 0.0.0.0 or send to 127.0.0.1.

See HTTPS & TLS for the certificate flags in context.

Web UI, REST API, and MCP

All three are served by one process on one port.

Flag Environment variable Default Description
-w, --web <port> MAILDEV_WEB_PORT 1080 Port for the web UI, REST API, and MCP endpoint
--web-ip <address> MAILDEV_WEB_IP 0.0.0.0 Address to bind the web server to
--web-user <user> MAILDEV_WEB_USER HTTP basic auth username for the UI and API
--web-pass <password> MAILDEV_WEB_PASS HTTP basic auth password for the UI and API
--base-pathname <path> MAILDEV_BASE_PATHNAME / Serve everything under this prefix
--disable-web MAILDEV_DISABLE_WEB off Do not serve the web UI at all
--https MAILDEV_HTTPS off Serve the web UI over TLS
--https-key <path> MAILDEV_HTTPS_KEY TLS private key for the web server
--https-cert <path> MAILDEV_HTTPS_CERT TLS certificate for the web server

Setting --web-user and --web-pass puts HTTP basic auth in front of the UI and the whole REST API. /api/healthz stays unauthenticated on purpose, so container health checks keep working.

--base-pathname /maildev moves the UI, the API (/maildev/api), and the MCP endpoint (/maildev/mcp) under that prefix. That is what you want behind a reverse proxy — see HTTPS & TLS for an nginx example, including the websocket headers the live inbox needs.

Storage

Flag Environment variable Default Description
--mail-directory <path> MAILDEV_MAIL_DIRECTORY Persist mail to disk. In-memory only when unset
--max-emails <count> MAILDEV_MAX_EMAILS 0 Keep at most this many messages. 0 means unlimited

Without --mail-directory, messages live in memory and are gone when the process exits. With it, messages are written as files and restored on startup — so restarting MailDev keeps your inbox.

--max-emails bounds the store: once the limit is reached, the oldest message is discarded, and its files with it. Worth setting whenever you persist to disk or run a long test suite.

Relay & auto-relay

MailDev can forward a caught message on to a real SMTP server — either on demand from the UI and API, or automatically for everything it receives.

Flag Environment variable Description
--outgoing-host <host> MAILDEV_OUTGOING_HOST Relay host
--outgoing-port <port> MAILDEV_OUTGOING_PORT Relay port
--outgoing-user <user> MAILDEV_OUTGOING_USER Relay username
--outgoing-pass <password> MAILDEV_OUTGOING_PASS Relay password
--outgoing-secure MAILDEV_OUTGOING_SECURE Use TLS for the relay connection
--auto-relay [email] Relay every message automatically, optionally overriding the recipient
--auto-relay-rules <path> JSON file of allow/deny rules for auto-relay

With only the --outgoing-* flags set, nothing is forwarded until you ask for it — per message, from the UI or via POST /api/email/:id/relay.

--auto-relay forwards everything as it arrives. Given an address (--auto-relay you@example.com) it rewrites the recipient, which is how you get every test message into one real inbox.

Rules narrow that down. The last matching rule wins:

[
  { "allow": "*" },
  { "deny": "*@test.com" },
  { "allow": "ok@test.com" },
  { "deny": "*@utah.com" },
  { "allow": "johnny@utah.com" }
]

That relays everything except @test.com and @utah.com addresses, with ok@test.com and johnny@utah.com allowed back through.

maildev --outgoing-host smtp.gmail.com --outgoing-secure --outgoing-user you@gmail.com --outgoing-pass secret --auto-relay --auto-relay-rules relay-rules.json

Auto-relay sends real email to real people. Rules are the only thing standing between a test run and a customer's inbox — start from { "deny": "*" } and allow specific addresses, rather than the other way round.

MCP

Flag Environment variable Default Description
--mcp MAILDEV_MCP off Serve the MCP endpoint at /mcp

See the MCP guide for client configuration, and the standalone maildev-mcp stdio server for desktop AI tools.

Logging

Flag Environment variable Default Description
-v, --verbose MAILDEV_VERBOSE off Verbose logging
--silent MAILDEV_SILENT off Suppress all output
--log-mail-contents off Log a JSON representation of every received message

--log-mail-contents prints message bodies to stdout. Convenient when debugging a parser problem; do not leave it on in a shared log.

Other

Flag Description
--config <path> Load configuration from a specific file
-V, --version Print the version and exit
-h, --help Print usage and exit

A handful of flags have no environment-variable equivalent in 3.0 — --auto-relay, --auto-relay-rules, --hide-extensions, --log-mail-contents, --incoming-cert, and --incoming-key. Set those on the command line, or in a configuration file, which is the better answer in Docker anyway.

Examples

A persistent inbox with MCP enabled and a bounded store:

maildev --mail-directory ./tmp/mail --max-emails 500 --mcp

Behind a reverse proxy at /maildev, with basic auth:

maildev --base-pathname /maildev --web-user dev --web-pass secret

CI: no UI, quiet, bounded:

maildev --disable-web --silent --max-emails 200

Different ports because something else owns the defaults:

maildev --smtp 2025 --web 8080

Edit this pageView as markdown