Issue Description
When Stalwart hits its open-file-descriptor limit (EMFILE), the accept loop in crates/common/src/network/listen.rs retries accept() immediately with no back-off, pinning every worker thread at ~100% CPU. All listeners (SMTP/IMAP/submission and the management port) go deaf while the process still looks “up”, and nothing is logged at normal levels. It only recovers on restart. Imprtant: its not config. the accept error emits a debug-only AcceptError event and loops with no sleep (verified on main/0.16.12), and the OS FD ceiling (1024, Docker default) is exhausted before Stalwart’s own maxConnections limiter (8192) so the graceful drop never runs and raising maxConnections can’t help.
Expected Behavior
On repeated accept() errors (especially EMFILE/ENFILE), back off with a short capped sleep instead of busy-looping, and log the failure at warn/error so it’s visible. Ideally document a recommended nofile ulimit for the Docker image
Actual Behavior
accept() fails every iteration, all workers busy-spin: load pins to the vCPU count (4.00 on 4 vCPU), all cores ~100%, every listener plus 127.0.0.1:8080 unresponsive, process still “up”, nothing at info/warn/error. Restart recovers in ~10s, then it re-wedges as FDs refill. Raising nofile to 65536 eliminated it.
Reproduction Steps
- Run Stalwart in a container with --ulimit nofile=1024:1024.
- Exhaust FDs: open connections faster than they close, or hold ~1024 open (in prod, a load balancer HTTPS health monitor on :443 leaked probe connections at ~6/min).
- All cores hit ~100%, every listener incl. management unresponsive, nothing logged.
- Restart recovers; it re-wedges at the limit.
Relevant Log Output
Nothing at default levels (that silence is part of the bug). At trace, roughly: NetworkEvent::AcceptError localPort = 443 reason = “Too many open files (os error 24)”
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
PostgreSQL
Blob Storage
S3-compatible
Search Engine
Internal
Directory Backend
Internal
Additional Context
Still present on main/0.16.12 (nothing in the 0.16.9–0.16.12 changelog fixes it). Triggered by a load-balancer health monitor leaking FDs at Docker-default nofile=1024. Distinct from forum #336 (NLB TLS-state reuse) and GitHub #863 (v0.10.4, not FD-related). Workaround: nofile raised to 65536.
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