Issue Description
Summary
Every TLS-terminated listener (IMAP/993, JMAP+HTTPS/443, SMTP submission) accepts the TCP connection and then closes it with zero bytes sent back — no TLS ServerHello, no plaintext banner, nothing — before any protocol handshake completes. This is not intermittent: it is 100% reproducible on every connection attempt, from every network path tested (public internet, host loopback, and container-to-container within the same Docker network).
The server has been in this state continuously. Docker’s own HEALTHCHECK (curl -k https://127.0.0.1:443/healthz/live) intermittently reports “healthy,” which is misleading — it is not representative of whether real protocol traffic (IMAP LOGIN, JMAP session, SMTP) can complete.
Environment
Deployed via the official stalwartlabs/stalwart Docker image, using the standard docker-compose.yml pattern from the Stalwart install docs (RocksDB backend, config.json as a datastore-pointer bootstrap file, all real config/domains/certificates stored in the RocksDB-backed registry).
Started on v0.16.12, running continuously for ~7 weeks before the fault was noticed.
Docker: docker compose v2, host is a standard Linux VPS (no unusual kernel/network config — verified: no fail2ban, no ufw, no custom iptables DROP/REJECT rules; iptables -L DOCKER-USER is empty; packet counters confirm inbound SYNs are correctly DNAT’d to the container).
What I ruled out, with direct evidence, before filing this
Resource exhaustion — checked /proc//limits and open FD count on the main process directly: 54 of 65,535 file descriptors in use. Memory ~360MB/7.7GB. CPU idle. Disk 17% used. None of these are close to any limit.
OS/network-namespace state — did a full docker compose down && up (not restart), which tears down and recreates the network namespace, port bindings, and iptables NAT rules from scratch. Identical failure afterward.
Binary version — upgraded from stalwartlabs/stalwart:v0.16.12 to :latest (resolved to v0.16.19) via the same procedure (down, swap image tag, up), same data volume. Identical failure on the new binary, immediately after a completely fresh process start.
Host firewall / connection filtering — fail2ban is inactive (not even installed as a running service), ufw is inactive, iptables -L DOCKER-USER -n -v is empty (0 rules), and the NAT table’s DNAT rules for 993/443/etc. show real packet/byte counters incrementing on test connections, confirming traffic reaches the container’s actual listening socket.
Certificate problems — used the built-in --console Data Store CLI to scan the SUBSPACE_REGISTRY (‘s’) keyspace directly (source-verified subspace byte from crates/store/src/lib.rs), extracted all 88 stored leaf certificates + chains (352 total BEGIN/END CERTIFICATE blocks), and decoded every one with openssl x509 -noout -subject -ext subjectAltName -dates. Findings:
No expired certificates. All notAfter dates are in the future relative to server time.
No duplicate/conflicting certificates for the same SAN set (checked specifically, since this pattern is described in a support thread as a known failure mode — not present here).
The specific certificate needed for the actual IMAP/JMAP hostname (CN=mx1.maillan.com, SAN mail.maillan.com, mx1.maillan.com) is present, singular, and currently valid (notBefore=Jul 9 2026, notAfter=Oct 7 2026).
A server.proxy.trusted-networks misconfiguration — this failure signature (connection accepted, then dropped silently expecting a PROXY-protocol header) is described in Stalwart’s own troubleshooting docs as an exact match. I searched the registry keyspace for any literal or namespaced form of this setting — nothing found. This deployment has no reverse proxy in front of Stalwart’s own published ports (Caddy in this stack only proxies two unrelated services on different ports; Stalwart’s 443/993/etc. are published directly).
No visibility from logging, at any level — this is the part I can’t get past without your help:
docker logs stalwart has been completely empty since the container was first created 7+ weeks ago (0 lines), across both v0.16.12 and v0.16.19.
Re-ran the server attached, with RUST_LOG=trace and RUST_BACKTRACE=full explicitly set, on a fresh container instance — made a real connection attempt against it — still zero stdout/stderr output, before or after the failed connection.
Checked whether telemetry/tracing is instead being written to Stalwart’s own internal store (SUBSPACE_TELEMETRY_SPAN, byte ‘o’, per crates/store/src/lib.rs) — scanned that entire keyspace directly via the console — it’s completely empty. Tracing does not appear to be enabled by default, and I have no way to enable it (the WebUI/JMAP admin API needed to configure it is the exact thing that’s unreachable due to this bug — a hard catch-22).
What this points to
Given the data is verified clean and the fault survives both a full environment recreate and a version upgrade, this looks like a runtime-level fault in how the server handles TLS/connection setup against this specific account/domain dataset (~280 mailboxes, ~20 domains with Automatic ACME certificate management via Let’s Encrypt, TlsAlpn01 challenge type) — not a configuration or data-corruption issue on my end. I’m not able to narrow it further without either the WebUI (blocked by this exact bug) or guidance on capturing meaningful diagnostics for a genuinely silent failure like this.
What would help
Is there a way to force verbose logging to stdout that doesn’t depend on RUST_LOG/RUST_BACKTRACE (e.g. a config value that must be set inside the RocksDB-backed config itself, which I can’t reach right now)?
Is there a known failure mode where a large number of Automatic-managed ACME domains (each with 3 auxiliary SANs: mta-sts., autoconfig., autodiscover.) could cause the TLS acceptor to hang/panic per-connection during certificate selection, in a way that wouldn’t show up as data corruption?
Any recommended way to safely inspect/repair this from the CLI/console alone, without the WebUI, given the exact SUBSPACE_REGISTRY key structure for Certificate/Domain objects (I reverse-engineered the subspace byte from source but not the full object-key encoding within it)?
Happy to provide anything else — I have a full backup of the RocksDB data taken immediately before any of this troubleshooting, plus the complete decoded certificate inventory, if that’s useful for reproduction.
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on