Skip to content

Queues

The queue holds every message that Stalwart has accepted but not yet finished delivering. It separates receiving a message from delivering it: the server can accept mail as fast as clients send it, and deliver it at the pace that remote servers and its own resources allow. Knowing how the queue is organised matters when one kind of traffic starts to delay another, or when the server must deliver more mail at once.

Every message that Stalwart accepts for delivery passes through the queue, whether it goes to a local mailbox or to another server. The message is written to the queue before the client receives its 250 reply. It stays in the queue until every recipient has been delivered, has failed permanently, or has expired.

Queued messages are stored in the data store, not in the memory of the node that received them. A restart therefore loses nothing. In a cluster, the queue is shared as well, so a message accepted by one node can be delivered by any node that runs the outboundMta task.

A single queue with a single pool of delivery threads would let one kind of traffic hold up all the others. For this reason the queue is divided into virtual queues, each with its own set of delivery threads. A backlog of mail to a slow remote domain then does not delay local delivery, and a burst of bounces does not delay user mail.

Each recipient is assigned to a virtual queue by its delivery schedule. The schedule is in turn selected by the schedule expression of the outbound strategy, so the expression decides indirectly which queue delivers each recipient.

Each virtual queue is an MtaVirtualQueue object (found in the WebUI under Settings › MTA › Outbound › Virtual Queues). Its name has at most eight characters. Its threadsPerNode is the number of deliveries the queue can perform at the same time on each node, and it is the setting that sizes the queue. Each delivery thread holds one connection to a remote server for the duration of a delivery. The number of threads therefore bounds two things at once: the throughput of the queue, and the number of outbound connections it opens.

A new installation has four virtual queues, one for each kind of traffic that the default schedules separate:

QueueThreads per nodeTraffic
local25Delivery to local mailboxes.
remote50Delivery to other servers.
dsn16Delivery status notifications.
report8DMARC and TLS reports.

The following object defines a queue with 200 threads per node for transactional mail:

{
"name": "transact",
"description": "Transactional mail",
"threadsPerNode": 200
}

A delivery schedule refers to its queue by the queue’s identifier, in its queueId field, not by the queue’s name. When a schedule’s queueId names a queue that does not exist, the schedule is reported as a configuration error, Virtual queue ID '<id>' does not exist., and is not loaded. The recipients that the expression assigns to that schedule then get the built-in fallback schedule (Missing strategies).

A queue can also be deleted while recipients are still waiting in it. Such recipients are delivered through a fallback queue with 25 threads. Tuning mail throughput describes how to size the queues.

Administrators can list, inspect, reschedule and cancel queued messages, and can pause and resume queue processing for the whole cluster. Queue management describes these operations.