Receiving mail

What happens while another server delivers mail on port 25 — connection rules, recipient checks, authentication, filtering — and every reply.

Mail for your domains arrives on port 25, handled by the mx role. Nixt Server makes every decision it can while the sending server is still connected. A message is either refused with a reason the sender sees at once, or accepted and stored durably before the server says so. The server never accepts a message and bounces it later because a recipient does not exist.

The steps

  1. Connection. Connection limits and a short pause before the greeting.
  2. Greeting and EHLO. The server says what it supports.
  3. STARTTLS. The sender can encrypt the conversation.
  4. Sender and recipients. Each recipient is checked against the directory.
  5. The message. Size and line-ending rules while it arrives.
  6. Authentication. SPF, DKIM, DMARC, ARC and reverse DNS.
  7. Filtering. The filter pipeline decides to deliver, tag, quarantine, refuse or defer.
  8. Acceptance. The message and its queue entry are written, and the sender is told the queue id.
  9. Delivery. The message goes into each recipient’s mailboxes, through their Sieve script.

1. Connection

RuleValueWhat the sender sees
Connections at once1,000421 4.7.0 Too many connections; try again later
Connections from one address20421 4.7.0 Too many connections from your address
Connections from one network200421 4.7.0 Too many connections from your network
Connections opened per address or networkNo rate unless you set one421 4.7.0 Too many connections from your address; try again later
Pause before the greeting2 secondsA client that talks before the greeting is refused: 554 5.5.0 Speak after the greeting
Silence between commands5 minutes421 4.4.2 Idle timeout; closing
Whole session1 hourThe connection is closed.
A network that kept guessing at addressesRefused for 1 hour421 4.7.0 Too many recipients refused from your network; try again later, before any greeting. See Guessing at addresses.

A network is an IPv4 /24 or an IPv6 /48. Change these ceilings, add rates, or exempt your own ranges in [listeners.mx]; see Connection ceilings. Behind a load balancer, set proxy_protocol = true on [listeners.mx] so these rules and the recorded client address use the real sender’s address.

2. Greeting and EHLO

220 mail.example.com ESMTP Nixt Server

The EHLO reply lists:

ExtensionMeaning
PIPELININGCommands may be sent together.
SIZE 26214400The largest message accepted, in bytes.
8BITMIME8-bit message bodies.
SMTPUTF8Internationalised addresses and headers.
ENHANCEDSTATUSCODESEvery reply carries a status code such as 5.1.1.
DSNDelivery status notification parameters.
CHUNKING and BINARYMIMEThe message may be sent in BDAT chunks, including binary bodies.
LIMITS RCPTMAX=100 MAILMAX=100Recipients per message, and messages per connection.
STARTTLSBefore TLS starts.
REQUIRETLSAfter TLS starts.
HELPA summary of the commands.

Port 25 never offers AUTH or BURL. VRFY answers 252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery, and EXPN and ETRN answer 502 5.5.1 Command not implemented.

The numbers in SIZE and LIMITS are the ceilings in force when the connection opened, including any lowered in runtime settings, and they hold for the whole connection.

3. STARTTLS

The sender may start TLS with the node’s certificate. If the node has no certificate yet — while an ACME certificate is on its way — the handshake is refused rather than the conversation continuing without encryption.

4. Sender and recipients

MAIL FROM is refused when:

ReplyCause
552 5.3.4 Message size exceeds limit of 26214400 bytesThe declared size is over the ceiling.
530 5.7.10 REQUIRETLS requires a TLS sessionREQUIRETLS was asked for without TLS.
503 5.5.1 Send EHLO firstNo EHLO yet.

Each RCPT TO is looked up in the directory: the domain’s tenant, then the address, then groups and aliases.

ReplyCause
250 2.1.5 Recipient OKA user, resource, group or alias here that can receive mail.
554 5.7.1 Relay access deniedThe domain is not hosted on this server. Port 25 never relays.
550 5.1.1 No such user hereThe domain is hosted, but nobody has the address.
550 5.2.1 Mailbox disabledThe account is disabled or deprovisioned, or the sender is not allowed to write to the group.
452 4.2.2 Mailbox full; try laterThe recipient’s mailbox is at its ceiling, or its tenant is suspended. The sender tries again later.
452 4.3.1 The organisation's mail storage is full; try laterThe recipient’s organisation is at its storage ceiling. The sender tries again later.
452 4.5.3 Too many recipientsThe message already names the most recipients allowed.
451 4.3.0 Directory unavailable; try laterThe store did not answer.

The addresses dmarc@ and tlsrpt@ are accepted for every hosted domain even when no account has them, so reports your DNS records ask for are not refused.

Guessing at addresses

A sender that tries one address after another, to learn from the replies which exist, is slowed down, then disconnected, and then refused. Each recipient refused for good in one connection counts:

Recipients refused in one connectionWhat happens
1 to 5Nothing more. A sender with an old list makes a few mistakes.
6 to 19Each refusal waits a tenth of a second longer than the one before, up to a second.
20The connection is closed: 421 4.7.0 Too many recipients refused

When connections from one network — a /24 for IPv4, a /64 for IPv6 — are closed this way three times within an hour, the network is refused new connections on port 25 for an hour. It is told so before the greeting, and may try again once the hour is over, or sooner if the operator lifts the lock:

421 4.7.0 Too many recipients refused from your network; try again later

Only permanent refusals count, such as 550 5.1.1 No such user here. A temporary one — a suspended tenant, a directory that did not answer — says nothing about whether an address exists, and the sender is right to try it again. A client connecting over IPv6 with an IPv4 address is counted under its own /24. Devices that send without signing in, and your own mailboxes servers and edge nodes, which are known by their certificates, are never counted or refused.

Each network refused this way is one line in the installation’s audit log, with the role receiving, naming the network and until when it is refused. vsx_sessions_closed_total{listener="mx"} counts the connections closed, by reason: harvesting for a connection closed at twenty refusals, network-refused for one refused before its greeting. The refused recipients themselves are in the message trace.

5. The message

The message arrives with DATA or in BDAT chunks.

ReplyCause
354 End data with <CR><LF>.<CR><LF>Ready for the message.
552 5.3.4 Message size exceeds limit of <n> bytesThe message grew past the ceiling.
550 5.6.0 Bare <LF> received; see RFC 5321 section 2.3.8A line ended with a line feed alone.
550 5.6.0 Bare <CR> received; see RFC 5321 section 2.3.8A carriage return not followed by a line feed.
550 5.6.0 NUL byte receivedA zero byte in the text.
550 5.6.0 Line too long; see RFC 5321 section 4.5.3.1.6A line over 8,192 bytes.

Line endings are checked strictly because loose handling is how one message can be smuggled inside another. A binary body sent with BDAT and BODY=BINARYMIME is not checked for line endings.

6. Authentication

The server checks the message against the sender’s published records:

CheckWhat it looks at
SPFWhether the connecting address may send for the envelope sender’s domain.
DKIMEvery signature on the message.
DMARCWhether SPF or DKIM passed for the domain in the From header, and that domain’s policy, found by walking up the domain tree when the domain itself publishes none. When the From header names several domains, each is checked.
ARCThe chain of seals on mail that has been forwarded.
Reverse DNSWhether the connecting address has a name that resolves back to it.

The results are written into an Authentication-Results header on the stored message and passed to the filter as a score.

When DMARC fails and the sender’s domain publishes p=reject, the message is refused:

550 5.7.1 Refused by the sender domain's DMARC policy

A message with more than one From header, or whose From header names more than five domains, is refused whatever the domains publish:

550 5.7.1 A message has one From header
550 5.7.1 The From header names more than 5 domains

Every message is also counted towards the aggregate report the sender’s domain asked for, if it asked for one. See Email authentication.

7. Filtering

The filter pipeline scores the message and may decide its fate:

DecisionWhat the sender seesWhat happens to the message
Deliver250 2.0.0 queued as <id>Delivered normally.
Tag250 2.0.0 queued as <id>Delivered to Junk, marked as junk.
Quarantine250 2.0.0 queued as <id>Kept in the hidden Quarantine mailbox.
Refuse550 5.7.1 <reason>Not accepted.
Defer451 4.7.1 <reason>Not accepted now; the sender tries again later.

8. Acceptance

The message and its place on the queue are written in one transaction before the server replies:

250 2.0.0 queued as 01K5B7Z3QX9W4M2N8R6T0V1YAC

The 26-character queue id identifies the message in the message trace, in the queue view and in the log. If the store cannot take it, the reply is 451 4.3.0 Storage unavailable; try later.

The server adds a Received header, for example:

Received: from mx.sender.example ([198.51.100.7])
	by mail.example.com with ESMTPS id 01K5B7Z3QX9W4M2N8R6T0V1YAC
	for <alex@example.com>;
	Mon, 14 Sep 2026 12:00:00 +0000

ESMTPS means the message arrived over TLS, ESMTP that it did not. The for line appears when the message has one recipient.

9. Delivery into mailboxes

The relay role hands the message to the deliver role, which for each recipient:

  1. Creates the account’s standard mailboxes if they do not exist yet.
  2. Runs the account’s active Sieve script, if it has one: filing, flagging, redirecting, rejecting, notifying and replying. See Sieve filters and vacation replies.
  3. Files the message: into Junk if the filter tagged it, into Quarantine if it quarantined it, otherwise where the script put it, or Inbox.
  4. Sends an out-of-office reply if one is set and the message qualifies.
  5. Tells every IMAP IDLE session and JMAP push connection for the account about the new message.

Delivery is one transaction per message, and a message delivered once is not delivered twice if the node restarts part way through.

A recipient that no longer resolves when delivery runs — because the account was deleted in the meantime — is sent back to the sender as undeliverable with 550 5.1.1 mailbox does not exist, when the sender asked for failure notices and passed SPF or DKIM.

Other replies

ReplyCause
421 4.7.0 Too many errors10 malformed or out-of-order commands on one connection.
421 4.7.0 Too many recipients refused20 recipients refused for good on one connection. See Guessing at addresses.
421 4.7.0 Too many commandsMore than 1,000 commands on one connection.
421 4.7.0 Too many messages in this connectionMore than 100 messages on one connection.
500 5.5.1 Command unrecognizedAn unknown command.
501 5.1.7 Bad sender address syntax, 501 5.1.3 Bad recipient address syntaxAn address the grammar cannot read.
555 5.5.4 Unsupported parameter: <parameter>A MAIL FROM parameter the server does not take.
451 4.3.0 Internal policy errorSomething unexpected went wrong; the sender tries again.

Watching incoming mail

  • vsx_messages_accepted_total{role="mx"} and vsx_messages_refused_total{role="mx",reason="filter"} count messages; see Monitoring.
  • vsx_sessions_closed_total{listener="mx"} counts connections closed or refused for guessing at addresses.
  • The log line accepted carries the queue id, the number of recipients and the size, and the line filtered carries the verdict, the stage that decided and the score. Neither ever carries message content.
  • versealx-server admin get trace tenant=1 address=alex@example.com day=2026-09-14 lists the messages an address received that day, and the ones refused for it.

Something unclear or out of date on this page? Tell us.