SMTP/LMTP/Submission endpoint

Module 'smtp' is a listener that implements ESMTP protocol with optional authentication, LMTP and Submission support. Incoming messages are processed in accordance with pipeline rules (explained in Message pipeline section below).

smtp tcp:// {
    tls /etc/ssl/private/cert.pem /etc/ssl/private/pkey.key
    io_debug no
    debug no
    insecure_auth no
    read_timeout 10m
    write_timeout 1m
    max_message_size 32M
    max_header_size 1M
    auth pam
    defer_sender_reject yes
    dmarc yes
    smtp_max_line_length 4000
    limits {
        endpoint rate 10
        endpoint concurrency 500

    # Example pipeline ocnfiguration.
    destination {
        deliver_to &local_mailboxes
    default_destination {

Configuration directives

hostname string

Default: global directive value

Server name to use in SMTP banner.

220 ESMTP Service Ready

tls certificate-path key-path { ... }

Default: global directive value

TLS certificate & key to use. Fine-tuning of other TLS properties is possible by specifying a configuration block and options inside it:

tls cert.crt key.key {
    protocols tls1.2 tls1.3

See TLS configuration / Server for details.

io_debug boolean

Default: no

Write all commands and responses to stderr.

debug boolean

Default: global directive value

Enable verbose logging.

insecure_auth boolean

Default: no (yes if TLS is disabled)

Allow plain-text authentication over unencrypted connections. Not recommended!

read_timeout duration

Default: 10m

I/O read timeout.

write_timeout duration

Default: 1m

I/O write timeout.

max_message_size size

Default: 32M

Limit the size of incoming messages to 'size'.

max_header_size size

Default: 1M

Limit the size of incoming message headers to 'size'.

auth module-reference

Default: not specified

Use the specified module for authentication.

defer_sender_reject boolean

Default: yes

Apply sender-based checks and routing logic when first RCPT TO command is received. This allows maddy to log recipient address of the rejected message and also improves interoperability with (improperly implemented) clients that don't expect an error early in session.

max_logged_rcpt_errors integer

Default: 5

Amount of RCPT-time errors that should be logged. Further errors will be handled silently. This is to prevent log flooding during email dictionary attacks (address probing).

max_received integer

Default: 50

Max. amount of Received header fields in the message header. If the incoming message has more fields than this number, it will be rejected with the permanent error 5.4.6 ("Routing loop detected").

buffer ram
buffer fs path
buffer auto max-size path

Default: auto 1M StateDirectory/buffer

Temporary storage to use for the body of accepted messages.

smtp_max_line_length integer

Default: 4000

The maximum line length allowed in the SMTP input stream. If client sends a longer line - connection will be closed and message (if any) will be rejected with a permanent error.

RFC 5321 has the recommended limit of 998 bytes. Servers are not required to handle longer lines correctly but some senders may produce them.

Unless BDAT extension is used by the sender, this limitation also applies to the message body.

dmarc boolean

Default: yes

Enforce sender's DMARC policy. Due to implementation limitations, it is not a check module.

Note: Report generation is not implemented now.

Note: DMARC needs SPF and DKIM checks to function correctly. Without these, DMARC check will not run.

Rate & concurrency limiting

limits { ... }

Default: no limits

This allows configuring a set of message flow restrictions including max. concurrency and rate per-endpoint, per-source, per-destination.

Limits are specified as directives inside the block:

limits {
    all rate 20
    destination concurrency 5

Supported limits:

scope rate burst period

Rate limit. Restrict the amount of messages processed in period to burst messages. If period is not specified, 1 second is used.

scope concurrency max

Concurrency limit. Restrict the amount of messages processed in parallel to max.

For each supported limitation, scope determines whether it should be applied for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or per-recipient domain ("destination"). Having a scope other than "all" means that the restriction will be enforced independently for each group determined by scope. E.g. "ip rate 20" means that the same IP cannot send more than 20 messages per second. "destination concurrency 5" means that no more than 5 messages can be sent in parallel to a single domain.

Note: At the moment, SMTP endpoint on its own does not support per-recipient limits. They will be no-op. If you want to enforce a per-recipient restriction on outbound messages, do so using 'limits' directive for the 'table.remote' module

It is possible to share limit counters between multiple endpoints (or any other modules). To do so define a top-level configuration block for module "limits" and reference it where needed using standard & syntax. E.g.

limits inbound_limits {
    all rate 20

smtp smtp:// {
    limits &inbound_limits

submission tls:// {
    limits &inbound_limits

Using an "all rate" restriction in such way means that no more than 20 messages can enter the server through both endpoints in one second.

Submission module (submission)

Module 'submission' implements all functionality of the 'smtp' module and adds certain message preprocessing on top of it, additionally authentication is always required.

'submission' module checks whether addresses in header fields From, Sender, To, Cc, Bcc, Reply-To are correct and adds Message-ID and Date if it is missing.

submission tcp:// tls:// {
    # ... same as smtp ...

LMTP module (lmtp)

Module 'lmtp' implements all functionality of the 'smtp' module but uses LMTP (RFC 2033) protocol.

lmtp unix://lmtp.sock {
    # ... same as smtp ...

Limitations of LMTP implementation