Skip to main content
Version: 0.16

In-memory store

In-memory data stores (such as Redis) are high-performance databases that keep data entirely in RAM, making read and write operations extremely fast. They are well suited to caching, message brokering, and short-lived state that does not need long-term persistence.

Within Stalwart, the in-memory store backs a range of short-lived data:

  • Rate limiting and fail2ban counters, such as the number of messages sent by a given sender or the number of failed authentication attempts from an IP.
  • Distributed locks that coordinate concurrent tasks, including account purge, email queue processing, and housekeeping.
  • OAuth authorisation codes used during the authorisation flow.
  • ACME tokens for SSL/TLS certificate management.
  • Short-lived tracking data such as greylisting tokens and Sieve auto-responder IDs.

Backends

Backend selection is made on the InMemoryStore singleton (found in the WebUI under Settings › Storage › In-Memory Store). The object is a multi-variant type. The supported variants are:

  • Redis, Valkey, Memcached, or compatible: the Redis and RedisCluster variants, recommended for high-performance, distributed, or heavily loaded environments.
  • The Default variant, which uses the configured data store (SQL, FoundationDB, RocksDB) as the in-memory store. This is a convenient choice when minimising external dependencies, though Redis is better suited to heavy write loads and distributed setups.
  • The Sharded variant, which distributes key-value pairs across multiple Redis or Redis Cluster backends by hashing keys modulo the number of configured stores. See the Sharded in-memory store page for further detail.

Key prefixes

In-memory stores operate as key-value stores. Stalwart assigns a unique integer prefix to each category of data so that keys never collide across subsystems. The mapping is fixed and shown below for reference:

Short nameDescriptionInteger prefix
KV_ACMEACME tokens for SSL/TLS certificate management0
KV_OAUTHOAuth authorisation codes1
KV_RATE_LIMIT_RCPTRate limiting data for recipient addresses2
KV_RATE_LIMIT_SCANRate limiting data for email scanning3
KV_RATE_LIMIT_LOITERRate limiting data for idle connections4
KV_RATE_LIMIT_AUTHRate limiting data for authentication attempts5
KV_RATE_LIMIT_SMTPRate limiting data for SMTP throttles6
KV_RATE_LIMIT_CONTACTRate limiting data for contact forms7
KV_RATE_LIMIT_HTTP_AUTHENTICATEDRate limiting data for authenticated HTTP requests8
KV_RATE_LIMIT_HTTP_ANONYMOUSRate limiting data for anonymous HTTP requests9
KV_RATE_LIMIT_IMAPRate limiting data for IMAP connections10
KV_QUOTA_BLOBBlob-quota accounting11
KV_GREYLISTSpam filter greylist tokens16
KV_LOCK_QUEUE_MESSAGELock for SMTP message queues21
KV_LOCK_TASKLock for background tasks23
KV_LOCK_DAVWebDAV locks25
KV_SIEVE_IDSieve auto-responder tracking ids26

This structured approach ensures data integrity and prevents key collisions across subsystems within the in-memory store.

Data persistence

Most keys held in the in-memory store are transient: rate-limit counters, fail2ban records, authorisation codes, and short-lived tokens do not need to survive a restart. Configuring persistence on the backend is therefore not usually required.

Configuration

To change the in-memory store backend, update the InMemoryStore singleton and select the appropriate variant. Variant-specific fields such as url and timeout apply to the Redis variant; urls, authUsername, authSecret, readFromReplicas, and protocolVersion apply to the RedisCluster variant. See the InMemoryStore reference for the full field list per variant.

Lookup keys for expressions and Sieve

Lookup data consumed by expressions and Sieve scripts is kept in the same InMemoryStore singleton; there is no separate named store per lookup. Individual entries are modelled as MemoryLookupKey (found in the WebUI under Settings › Lookups › In-Memory Keys, Settings › Spam Filter › Lists › Blocked Domains, Settings › Spam Filter › Lists › Spam Traps, Settings › Spam Filter › Lists › Trusted Domains, Settings › Spam Filter › Lists › URL Redirectors) for membership-style lookups and as MemoryLookupKeyValue (found in the WebUI under Settings › Lookups › In-Memory Key-Values) for key-value lookups. Each record carries its own namespace, so distinct lookup sets coexist within the single backend.

Maintenance

When a persistent data store (SQL, FoundationDB, RocksDB) is used as the in-memory store through the Default variant, expired keys must be purged periodically. This is handled by the general data-store clean-up task, scheduled through dataCleanupSchedule on the DataRetention object (found in the WebUI under Settings › Storage › Data Retention › Archiving, Settings › Storage › Data Retention › Data Cleanup, Settings › Storage › Data Retention › Auto-Expunge, Settings › Storage › Data Retention › Telemetry). When Redis or Redis Cluster is used, expiry is handled natively by the backend and no scheduled clean-up is required.