Listeners
Before a client can use any service, the server must accept its connection on some port. A listener is the part of the configuration that does this: it accepts TCP connections on one or more addresses and serves one protocol on them. Each listener is a NetworkListener object (found in the WebUI under Settings › Network › Listeners), and the server can run any number of them. Together, the listeners determine three things: which ports are open, whether each port starts with TLS, and how many clients each port accepts at once.
Default listeners
Section titled “Default listeners”At the first start after the setup wizard, the server creates a standard set of listeners. Each is bound to [::], which accepts IPv4 and IPv6 connections on all interfaces:
| Name | Port | Protocol | TLS |
|---|---|---|---|
smtp | 25 | SMTP | STARTTLS |
submissions | 465 | SMTP | Implicit |
imaps | 993 | IMAP | Implicit |
pop3s | 995 | POP3 | Implicit |
sieve | 4190 | ManageSieve | STARTTLS |
https | 443 | HTTP | Implicit |
http | 8080 | HTTP | None |
The server creates these listeners whenever no listener exists at startup. Deleting some of them is therefore permanent, but deleting all of them brings back the whole default set at the next start.
The set contains no listener for submission with STARTTLS on 587, IMAP on 143 or POP3 on 110. These are added by hand when needed, as described in Adding a listener.
On a host without IPv6, an unspecified IPv6 bind address falls back to 0.0.0.0, and the server logs the fallback as a warning.
The listeners are not started during bootstrap and recovery mode. In those modes, the server opens only a plain HTTP listener on port 8080, or the port set by STALWART_RECOVERY_MODE_PORT. That listener is always bound to [::], and its address cannot be changed.
Bind addresses and protocol
Section titled “Bind addresses and protocol”Each listener answers two questions: where it accepts connections, and what it speaks on them.
bind is the set of addresses and ports the listener accepts connections on. Because it is a set, one listener can bind several IPv4 and IPv6 addresses. protocol is one of smtp, lmtp, http, imap, pop3 and manageSieve.
The following listener accepts SMTP on two IPv4 addresses and one IPv6 address:
{ "name": "smtp", "protocol": "smtp", "bind": { "192.0.2.1:25": true, "203.0.113.9:25": true, "[2001:db8::2]:25": true }}The lmtp protocol accepts deliveries over LMTP, a variant of SMTP meant for handing messages to a final mail store. It serves another mail system that delivers to Stalwart, for example a front-end MTA that hands messages to Stalwart for local delivery. No default listener serves it, and it is normally bound to an internal address only.
Implicit TLS and STARTTLS
Section titled “Implicit TLS and STARTTLS”Mail protocols use TLS in one of two modes, and each port expects one of them:
- With implicit TLS, the connection starts with a TLS handshake, before any protocol data. Ports 465, 993, 995 and 443 work this way.
- With STARTTLS, the connection starts in plain text, and the client upgrades it to TLS with a protocol command. Ports 25, 587, 143, 110 and 4190 work this way.
RFC 8314 prefers implicit TLS for client access. Server-to-server SMTP on port 25 has no such choice, because STARTTLS is the only mode available there.
Two fields select the mode:
useTlsenables TLS on the listener. When it isfalse, the listener offers no TLS at all, so SMTP, IMAP, POP3 and ManageSieve do not advertise STARTTLS.implicitTlsstarts every connection with a TLS handshake.
A listener created with neither field set has TLS enabled and implicit TLS disabled, which makes mail protocols offer STARTTLS.
HTTP has no STARTTLS, so for HTTP only implicitTls matters. An HTTP listener with implicitTls set to false serves plain HTTP, whatever useTls is set to. HTTPS needs implicitTls set to true.
tlsTimeout limits the time a client has to complete the handshake. The protocol versions and cipher suites that a listener accepts are also set per listener, as described in TLS. The certificate, on the other hand, is not a listener setting: it is chosen from the name the client requests, as described in Certificates.
Adding a listener
Section titled “Adding a listener”Ports that the default set does not cover are opened by adding a listener. The following objects add submission with STARTTLS on port 587 and IMAP with STARTTLS on port 143:
[ {"name": "submission", "protocol": "smtp", "bind": {"[::]:587": true}}, {"name": "imap", "protocol": "imap", "bind": {"[::]:143": true}}]Neither object sets useTls or implicitTls, so both listeners offer STARTTLS.
Opening a port does not make clients use it. To advertise the new ports to clients in SRV records and autoconfiguration, also set cleartext on the corresponding entry of SystemSettings.services.
Not every setup needs a new listener. A reverse proxy on the same host that connects over plain HTTP can use the default http listener, and a second listener on the same port would conflict with the existing one. To keep the default http listener out of reach of other hosts, restrict it to the loopback interface by changing its bind to {"127.0.0.1:8080": true}.
Connection limits
Section titled “Connection limits”maxConnections limits the number of concurrent connections that a listener accepts. All the listener’s bind addresses share the limit. When the field is unset, the listener uses SystemSettings.maxConnections, which is 8192 unless changed.
The limit applies per listener, so two listeners without their own limit can each hold that many connections. A connection beyond the limit is closed at once and logged as limit.concurrent-connection.
The following listener caps submissions at 1024 connections:
{ "name": "submissions", "protocol": "smtp", "bind": {"[::]:465": true}, "implicitTls": true, "maxConnections": 1024}PROXY protocol
Section titled “PROXY protocol”A proxy in front of the server hides the client’s address, and the PROXY protocol passes it on in a header at the start of each connection. Connections from the networks in SystemSettings.proxyTrustedNetworks must begin with such a header, and the server takes the client’s address from it. overrideProxyTrustedNetworks replaces that list for one listener. PROXY protocol describes the consequences for connections that do not send the header.
Socket options
Section titled “Socket options”Socket options tune how the operating system handles the listener’s connections. The following fields set them per listener. Their defaults, listed in the reference, suit most deployments:
socketBacklog: maximum number of connections waiting to be acceptedsocketNoDelay: disables the Nagle algorithmsocketReuseAddress: setsSO_REUSEADDRsocketReusePort: setsSO_REUSEPORTsocketSendBufferSizeandsocketReceiveBufferSize: socket buffer sizes in bytes, the operating system’s when unsetsocketTtl: IP time-to-live of outgoing packetssocketTosV4: IPv4 type-of-service value
Applying changes
Section titled “Applying changes”Listener changes, including new and deleted listeners, take effect only when the server restarts, because reloading the settings does not open or close ports.
In a cluster, a node starts only the listeners enabled for its role and skips those excluded by the role’s listeners setting.
A listener can fail to bind one of its addresses, because another process holds the port or because the server lacks the privilege to bind a port below 1024. The listener then skips that address, and the server continues to start. The failure is logged as a registry.build-error event for the listener, with the reason Failed to bind to <address>: <error>. Troubleshooting installation lists the usual causes.
STARTTLS not offered or TLS handshake fails
Section titled “STARTTLS not offered or TLS handshake fails”TLS problems on a listener usually come from a mismatch between the port, the client’s expectation and the listener’s TLS mode:
- The client expects STARTTLS but the server does not offer it. The listener has
useTlsset tofalse, or the client connects to an implicit-TLS port and sends plain text. Port 465 requires implicit TLS, and port 587 requires STARTTLS. - The handshake is closed immediately. The client connects to an implicit-TLS port with a plain-text protocol, or the other way round, or the client rejects the certificate because it does not cover the name the client connected to. A server without any certificate presents a self-signed certificate for
localhost, which clients reject. - The connection is closed before any data is exchanged. The client’s address is banned. The server drops connections from banned addresses before the TLS handshake and before the greeting. Auto-ban describes how to confirm and clear the ban.