Skip to content

Blob store

Most of the bytes a mail server stores belong to a small number of large objects: messages with their attachments, and the files users upload. The blob store holds these large binary objects, or blobs, apart from the structured records in the data store. The separation keeps the data store small and fast, and it allows the bulk of the data to be placed on inexpensive object storage.

Each blob is identified by its content rather than by the item that uses it. Identical contents are therefore stored only once, which matters for a message delivered to several accounts on the same server: every account refers to the same blob.

The blob store holds the following data:

  • Raw email messages, including their attachments.
  • Files stored by users through WebDAV and JMAP file storage.
  • Sieve scripts.
  • Spam classifier training samples and the trained model.
  • Blobs uploaded by JMAP clients that have not yet been attached to a message. Unused uploads expire and are removed by the blob purge.
  • Snapshots of account caches, when cache swapping uses the BlobStore backend with its default store.

Account records, mailboxes, flags, calendar events and contacts are structured data, so they are kept in the data store rather than here.

The backend is selected by the @type of the BlobStore object (found in the WebUI under Settings › Storage › Blob Store):

@typeWhere blobs are keptShared by cluster nodes
DefaultIn the data store (default)When the data store is shared
S3An S3-compatible bucketYes
AzureAn Azure Blob Storage containerYes
FileSystemA directory on the local file systemOnly on a shared file system
PostgreSql, MySql, FoundationDbA database separate from the data storeYes
ShardedSeveral of the above, with blobs distributed among them (composite stores, Enterprise)Yes

The Default variant is the simplest choice. Blobs then share the data store’s backups, replication and scaling, and no other service is needed. A separate blob store becomes worthwhile in two situations. The first is when the volume of message contents makes the data store too large to back up or replicate within the available time. The second is when object storage costs less than database storage. The fields of each variant are described on its backend page and in the BlobStore reference.

One combination needs care. The FileSystem backend cannot hold cache swap snapshots, so a server whose blob store is FileSystem uses the LocalFile or Redis swap backend instead.

A change to the BlobStore object selects where new blobs are written and where existing ones are looked up. It does not copy the blobs that are already stored. As a result, after a change the messages, files and scripts written before it can no longer be read. To change the blob store of a server that already holds data, migrate the data as described in Database migration. Like any store change, the new setting takes effect after a restart.

Blobs are compressed before they are written. The algorithm is set by the compressionAlgorithm field of the Email object (found in the WebUI under Settings › Email › Defaults, Settings › Email › Encryption, Settings › Email › Limits, Settings › Email › Storage), and out of the box it is lz4. The value lz4 applies LZ4 compression, which favours speed over compression ratio, and the value none stores blobs uncompressed.

A change of algorithm applies only to the blobs written afterwards. Existing blobs are read in the form in which they were written, so no conversion is needed.

A blob is deleted once nothing references it any more. This happens, for example, after the last message that uses it is expunged, or when an upload expires without being used. The deletion itself is performed by the blob purge, which runs daily at 04:00 unless its schedule is changed. Data retention and reclaiming disk space describes the schedule, and when each backend returns the freed space.