Connection and request limits
A server has finite resources, and connection and request limits decide how much of them any one client can hold. They bound open sockets per listener, sessions and parallel requests per account, and requests per minute. Setting them is a balance between two failures. A limit that is too low for legitimate use shows up as refused connections or throttling errors. A limit that is too high lets a single client, or a misbehaving one, occupy resources meant for everybody.
This page describes the limits in the order in which they are most often reached. Each protocol’s settings page lists the remaining fields.
Listener connection limits
Section titled “Listener connection limits”The first limit a client meets is the one on the listener it connects to. Each NetworkListener (found in the WebUI under Settings › Network › Listeners) accepts at most maxConnections connections at the same time, shared by all its bind addresses.
A listener without its own value uses maxConnections on SystemSettings, which is 8192 unless changed. That value applies to each listener separately and is not a total for the server. Five listeners without their own limit can therefore together hold five times as many connections.
Before the server accepts a new connection, the operating system queues it for the listener, and socketBacklog sets the length of that queue. A larger backlog absorbs bursts of new connections, such as many clients reconnecting after a network outage.
When a listener is at its limit, new connections are refused, and the limit.concurrent-connection counter and event increase. Both settings take effect after a restart, as described in Listeners.
The operating system imposes a limit of its own that must be planned with these. Every connection holds an open file, so the operating system’s limit on open files must cover the sum of all listener limits plus the files the server opens itself. The systemd unit installed by the installation script sets LimitNOFILE=65536.
Per-account limits for IMAP, POP3 and ManageSieve
Section titled “Per-account limits for IMAP, POP3 and ManageSieve”A listener limit protects the server as a whole; the per-account limits keep one account from taking more than its share. On the Imap object (found in the WebUI under Settings › Network › IMAP), maxConcurrentConnections limits the authenticated sessions of one account to 16 unless changed. The limit counts IMAP, POP3 and ManageSieve sessions together.
A login beyond the limit is refused, as described in Concurrent sessions, and the limit.concurrent-request event is emitted at the debug level. Legitimate use can reach this limit: some clients open one connection per folder, and a user with several devices opens sessions from each of them. Raise the limit to what those accounts need, but not to the point where one account can hold hundreds of sessions.
The second per-account limit is on the rate of commands. rateLimit limits the commands one account may issue per period, and it applies to IMAP, POP3 and ManageSieve. A client over the limit receives a temporary error, and the limit.too-many-requests counter increases. Accounts that hold the unlimitedRequests permission are exempt from the rate limit, but not from the session limit.
Both settings take effect after a settings reload, with one exception. maxConcurrentConnections is applied to an account when its session data is loaded into the access token cache. An account that is already cached therefore keeps the previous session limit until its entry is evicted, or until all caches are cleared with the InvalidateCaches action:
stalwart-cli create Action/InvalidateCachesVery large mailboxes need a different kind of protection, because a single command on them can be expensive. The settings that provide it, maxMessagesPerCommand, minUidBatchSize and maxUidBatches, are described in IMAP settings.
JMAP and HTTP limits
Section titled “JMAP and HTTP limits”JMAP clients send requests over HTTP, so they are bounded by two sets of limits: the per-account limits of JMAP and the rate limits of the HTTP server.
On the Jmap object (found in the WebUI under Settings › Network › JMAP › Limits, Settings › Network › JMAP › Push, Settings › Network › JMAP › WebSocket), maxConcurrentRequests and maxConcurrentUploads limit the requests and uploads that one account runs at the same time. Unless changed, they are 4 and 4. They are applied when the account’s session data is cached, so the exception described for the IMAP limits applies to them too. The size of each request is bounded separately, by result limits such as maxGetResults and maxQueryResults. These are listed with the other request limits in JMAP protocol settings.
On the Http object (found in the WebUI under Settings › Network › HTTP › General, Settings › Network › HTTP › Security), two rate limits apply. rateLimitAuthenticated limits the requests of each authenticated account, and rateLimitAnonymous limits unauthenticated requests per client IP address.
The anonymous limit depends on knowing the real client address. Behind a reverse proxy, the server takes the client address from the source set by clientIpSource. When that source is not set correctly, every request appears to come from the proxy, so all anonymous clients share one limit. HTTP settings describes how to set it.
A client that reaches a concurrency limit increases limit.concurrent-request or limit.concurrent-upload, and one that reaches a rate limit increases limit.too-many-requests. These settings take effect after a settings reload.
WebDAV and calendar limits
Section titled “WebDAV and calendar limits”WebDAV requests can ask for large amounts of data at once, so their limits bound the size of a single request rather than the number of requests. On the WebDav object (found in the WebUI under Settings › Network › WebDAV), maxResults limits the resources returned by one PROPFIND or REPORT, and maxRequestSize limits the size of a request body.
Calendars have limits of their own, set on the Calendar object (found in the WebUI under Settings › Calendar & Contacts › Calendar). A recurring event can produce an unbounded number of occurrences, so a query that expands recurrences is limited in time span. maxExpandedQueryDuration sets that span to 365 days unless changed. A JMAP query over a longer span fails with the expandDurationTooLarge error and emits the jmap.expand-duration-too-large event.
availabilityRateLimit limits the free/busy queries of each account, as described in Free/busy and availability. These settings take effect after a settings reload.
Worker threads
Section titled “Worker threads”The threads that run network and protocol work have no registry setting. The server starts one per CPU core, and only the TOKIO_WORKER_THREADS environment variable changes the number. The RocksDB and SQLite data stores run a separate pool, which is sized by their poolWorkers setting. Both pools are described in Worker threads.