2.87.1 (2026-11-05)
Summary
A reliability release. It stops a queued message from being sent twice when a send outlasts its timeout or a worker dies at the wrong moment, keeps exports alive across worker restarts, fixes the Docker health check for instances that serve the API over HTTPS, and keeps work that Redis refused for a moment instead of dropping it.
Who should upgrade
- Everyone sending through the outbox queue. A send that outlasted the command timeout was retried while the first SMTP transaction was still running, so the recipient could get the message twice. Queued messages are now sent under a lock and a retry of a message that already went out is counted as sent.
- Urgent for Docker Swarm deployments with
EENGINE_API_TLS. The image's health check probed a TLS port over plain HTTP, so Swarm never saw the task as healthy and replaced it every few minutes. This affected every release since 2.86.0. - Anyone running more than one export worker, or restarting export workers while exports run. A worker starting up could fail another worker's running export and delete its file.
Breaking changes / action required
- Export files written by this release use a new encryption format (version 3) that older releases cannot read. Files from earlier releases still decrypt.
Highlights
- Outbox: a queued message is sent once even when a send times out, a worker dies between the send and the job's completion, or Redis is at its memory limit at that moment.
- Exports: an export whose job is still queued or running is left to the queue at startup, and an export of a worker that crashed runs again within about three minutes instead of failing. Encrypted export chunks are now bound to their position in the file, so a reordered, truncated or spliced file is refused.
- Redis under pressure: a reply that clears up on its own (out of memory, loading, read-only) no longer drops new IMAP messages, partial syncs or Gmail and Microsoft Graph changes; the work is kept and retried.
- IMAP: a message moved, copied or uploaded into a folder created moments earlier gets its id, and
text.hasMoreis set correctly when a multibyte or CRLF body is cut atmaxBytes. - Account setup: Exchange autodiscover answers that carry
<Encryption>instead of<SSL>(IONOS) now set TLS correctly. - Admin login: an instance that got its first password from
emailengine passwordasks for a login right away instead of after a restart. - Templates: a template id that does not decode to a valid timestamp is reported as not found instead of failing with a server error.
Bug Fixes
- bind export encryption chunks to their position in the file (c138b61)
- export recovery ownership and sent-marker durability (2.87.1 review) (5c24113)
- keep the sent marker until the submit job is recorded as completed (aecf19f)
- make the Docker health check follow the API's TLS setting, port and bind address (957dec0)
- never fail an export another export worker is still running (74d1755)
- read the TLS mode from <Encryption> in Exchange autodiscover answers (5a1b7e9)
- report a template id with an out-of-range timestamp as not found (ac043db)
- set text.hasMore when an IMAP text part is cut at maxBytes (f070b5e)
- share the worker RPC, token check and new-message steps instead of copies (ac4cf28)
- standalone fixes for new-folder ids, Redis-refused work, duplicate sends, CLI password and queued exports (#685) (8e934c0)
- update dependencies (56df020)
- update dependencies (25ee28f)
- update dependencies (bullmq 6.5.4, fast-xml-parser 5.12.0) (5624ee1)