Retention & purge
Dispatch automatically ages out old data so the database and spool disk stay healthy. A purge runs every 6 hours, and you can trigger one on demand from Settings → Storage maintenance or with POST /api/purge/run.
Default retention
Section titled “Default retention”| Data | Retained for |
|---|---|
| Delivered log rows | 7 days |
| Failed log rows | 7 days |
Spool failed/ files |
7 days |
| Captured (Local Inbox) files | 7 days |
| Audit log | 7 days |
| Security audit entries | 7 days |
All of these are editable under Settings → Retention, and map to the purge.* keys in the config key reference.
Setting any retention to 0 means “keep forever” - that data type is never purged by age. (The optional size-based safety net below is disabled by default; it only applies if you turn it on.) Use 1 or more to actually delete by age.
Storage usage
Section titled “Storage usage”Settings → Storage → Usage (and GET /api/storage) shows what each retention category is actually consuming: message-log rows broken out by event (Delivered, Failed, Retrying, Test sends, Denied) with an estimated size, the audit log’s rows and size, and the on-disk spool directories (captured / retry-queue) with exact file counts and bytes - plus free space on the spool volume. Use it to decide which retention window to tighten. (Per-event database sizes are estimated from each event’s share of the message-log table; spool figures are exact.)
Size-based safety
Section titled “Size-based safety”In addition to age-based purging, Dispatch can watch the total database size and free space once it grows past a threshold you choose. This is an optional, opt-in safeguard that is disabled by default (purge.size_trigger_gb defaults to 0) - none of the supported engines (bundled SQLite, PostgreSQL, MariaDB/MySQL, SQL Server) impose a hard size cap, so nothing happens here unless you set a GB threshold yourself.
When you set purge.size_trigger_gb to a nonzero value, Dispatch checks the database’s actual size each purge cycle, using whichever engine-specific measurement the active backend supports. Once the database exceeds that threshold, it frees a bounded amount of space by removing the oldest history - message-log rows first, then audit rows if needed - down to purge.size_target_gb, then reclaims the freed space (VACUUM on SQLite/PostgreSQL, the equivalent on other engines).
Because this net can fire even when a retention is set to “keep forever”, it does not silently discard history: before deleting, it exports the rows to weekly JSON Lines files (e.g. relay_log-2026-W26.jsonl, audit_log-2026-W26.jsonl) under the spool’s archive/ directory. The exact counts are written to the System Logs as a “Size-pressure cleanup ran” entry. You can move those archive files off the box or re-ingest them later. They’re kept indefinitely by default; set Settings → Retention → Size-pressure archive retention to age them out automatically (0 = keep forever).
Spool disk protection
Section titled “Spool disk protection”Separately, Dispatch watches free disk space on the spool volume. As free space runs low it first throttles, then temporarily refuses new SMTP intake with a transient 4xx response. Because the rejection is transient, well-behaved senders simply retry, and intake resumes automatically once space recovers. This ensures a full disk can never corrupt the spool.