Inbound/Outbound Delivery is empty

Issue Description

In the UI Emails → History → Inbound / Outbound Delivery is empty.

Expected Behavior

I expect to see Emails that I’ve send/received in the history in order to see how the server behaves.

Actual Behavior

The view is empty.

(It has a odd “create inbound/outbound delivery” button, but that just returns a forbidden).

Reproduction Steps

  1. Configure stalwart. Add postgres as normal backend, and a second Postgres’s as tracing/metrics backend (as I don’t want to backup these things).
  2. Send a mail to an account on this server
  3. Wait for the mail to arrive (as seen in Apple Mail).
  4. Look into the admin UI under Emails → History → Inbound Deliveries

Relevant Log Output

I don’t see anything regarding this in the trace log (and I’m not sure what to redact to post a full log publicly).

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

PostgreSQL

Blob Storage

Filesystem

Search Engine

PostgreSQL

Directory Backend

Internal

Additional Context

This was also broken in 0.15 when using two different Postgres Backends and then fixed (although I always had to manually reindex the traces for it to show up and after a few minutes the view was empty again.)

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

That history view is populated from persisted delivery trace spans, which is an Enterprise telemetry-history feature; on Community edition the store tracer that writes them isn’t enabled, so the Inbound/Outbound Delivery lists stay empty and the create button returns forbidden. Pointing a second Postgres at tracing/metrics doesn’t change that on Community, the tracer still doesn’t run.

So if you’re on Community, the empty view is expected rather than broken (if it looked populated under 0.15 that would have been during an enterprise/trial window). Live telemetry under Troubleshoot still works for watching deliveries in real time. If you are on Enterprise and it’s still empty, let me know and we’ll look at the tracing store config.

Yes I’m on Enterprise (sorry forgot to mention that).

Here hopefully all relevant settings:

{"@type":"upsert","object":"Tracer","value":{"tracer-iy00032caaqa":{"@type":"Stdout","multiline":false,"eventsPolicy":"exclude","buffered":true,"level":"info","ansi":true,"enable":true,"lossy":false,"events":{}}}}
{"@type":"upsert","object":"Tracer","value":{"tracer-iy09kfoqaiab":{"@type":"Log","enable":true,"prefix":"stalwart.log","events":{},"lossy":false,"rotate":"daily","ansi":false,"level":"info","multiline":false,"eventsPolicy":"exclude","path":"/var/log/stalwart"}}}
{"@type":"update","object":"SystemSettings","value":{"threadPoolSize":null,"services":{"caldav":{"cleartext":false,"hostname":null},"carddav":{"cleartext":false,"hostname":null},"imap":{"cleartext":false,"hostname":null},"jmap":{"cleartext":false,"hostname":null},"managesieve":{"cleartext":false,"hostname":null},"pop3":{"cleartext":false,"hostname":null},"smtp":{"cleartext":false,"hostname":null},"webdav":{"cleartext":false,"hostname":null}},"defaultHostname":"pluto.spei.ch","mailExchangers":{"0":{"priority":10,"hostname":null}},"defaultCertificateId":null,"proxyTrustedNetworks":{},"maxConnections":8192,"providerInfo":{}}}
{"@type":"update","object":"DataRetention","value":{"archiveDeletedAccountsFor":null,"maxChangesHistory":10000,"dataCleanupSchedule":{"@type":"Daily","hour":2,"minute":0},"holdMtaReportsFor":2592000000,"metricsCollectionInterval":{"@type":"Hourly","minute":0},"expungeSchedulingInboxAfter":2592000000,"archiveDeletedItemsFor":null,"holdMetricsFor":7776000000,"holdTracesFor":2592000000,"expungeShareNotifyAfter":2592000000,"expungeSchedule":{"@type":"Daily","hour":0,"minute":0},"blobCleanupSchedule":{"@type":"Daily","hour":4,"minute":0},"expungeTrashAfter":2592000000,"expungeSubmissionsAfter":259200000}}
{"@type":"update","object":"BlobStore","value":{"@type":"FileSystem","depth":2,"path":"/data/blobs"}}
{"@type":"update","object":"InMemoryStore","value":{"@type":"Default"}}
{"@type":"update","object":"SearchStore","value":{"@type":"PostgreSql","allowInvalidCerts":false,"useTls":false,"poolRecyclingMethod":"fast","options":null,"authSecret":{"@type":"EnvironmentVariable","variableName":"POSTGRES_PASSWORD"},"database":"stalwart_nobackup","readReplicas":{},"authUsername":"stalwart","poolMaxConnections":10,"timeout":15000,"host":"db","port":5432}}
{"@type":"update","object":"TracingStore","value":{"@type":"PostgreSql","allowInvalidCerts":false,"useTls":false,"poolRecyclingMethod":"fast","options":null,"authSecret":{"@type":"EnvironmentVariable","variableName":"POSTGRES_PASSWORD"},"database":"stalwart_nobackup","readReplicas":{},"authUsername":"stalwart","poolMaxConnections":10,"timeout":15000,"host":"db","port":5432}}

If more information is needed, kindly tell me.

Your config looks right, so this isn’t the usual “store tracer not enabled” case. On Enterprise with a TracingStore set, the delivery-history tracer is provisioned automatically; there’s no store tracer you need to add by hand, and your Stdout/Log tracers with exclude/empty events don’t affect it.

The way it works internally: a delivery span is only persisted if it carries a queue id, and the history view reads those spans back from the search store after an indexing task runs. So an empty view with a valid TracingStore usually means the spans aren’t being indexed or are being pruned, rather than not being captured. Your 0.15 experience (manual reindex made them appear, then they vanished after a few minutes) fits that.

To narrow it down: after you set the tracing store, did any deliveries actually go through the queue, and do you see trace-indexing activity or errors in the logs? Also check holdTracesFor and the daily cleanup schedule aren’t pruning them almost immediately. If deliveries are happening and nothing is landing in the history within the retention window, this is one I want to look at on the indexing side rather than a config change on yours.

The data retention stuff seems alright to me:

{
  "expungeTrashAfter": 2592000000,
  "expungeSubmissionsAfter": 259200000,
  "expungeShareNotifyAfter": 2592000000,
  "expungeSchedulingInboxAfter": 2592000000,
  "expungeSchedule": {
    "hour": 0,
    "minute": 0,
    "@type": "Daily"
  },
  "dataCleanupSchedule": {
    "hour": 2,
    "minute": 0,
    "@type": "Daily"
  },
  "blobCleanupSchedule": {
    "hour": 4,
    "minute": 0,
    "@type": "Daily"
  },
  "maxChangesHistory": 10000,
  "archiveDeletedItemsFor": null,
  "archiveDeletedAccountsFor": null,
  "holdMtaReportsFor": 2592000000,
  "holdTracesFor": 2592000000,
  "holdMetricsFor": 2592000000,
  "metricsCollectionInterval": {
    "minute": 0,
    "@type": "Hourly"
  },
  "id": "singleton"
}

This is the default I’ve not changed anything, it should hold stuff for 30days (that’s wat the UI says at least).

Yes, with the configs shown I’ve had mails send/received (and therefore gone through the queue or is there a bypass?).

The Metrics Dashboard works fine, if that is of any use.

When a mail is delivered I see the following (amongst other stuff)

stalwart-1       | 2026-07-12T13:37:44Z INFO Task scheduled for future execution (task-manager.task-scheduled) due = 2026-07-12T13:42:43Z, id = "alertMetrics"
stalwart-1       | 2026-07-12T13:37:44Z INFO Task scheduled for future execution (task-manager.task-scheduled) due = 2026-07-12T13:42:43Z, id = "calculateMetrics"
stalwart-1       | 2026-07-12T13:37:44Z INFO Metrics collected (telemetry.metrics-collected) elapsed = 22ms

But nothing that points to tracing/indexing.

Looking at the db I see this:

stalwart_nobackup=> SELECT COUNT(*) FROM x;
 count
-------
  3667
(1 row)

stalwart_nobackup=> SELECT COUNT(*) FROM o;
 count
-------
  2731
(1 row)
stalwart_nobackup=> SELECT COUNT(*) FROM s_trace;
 count
-------
     0
(1 row)
stalwart_nobackup=> SELECT COUNT(*) FROM s_email LIMIT 1;
 count
-------
 46838
(1 row)

Something I noticed right now is that the cluster nodes are all stale (I only have a single instance, but a few nodes were created due to the changing hostname of docker compose containers before I fixed that).

Id  Node ID  Hostname        Last Renewal          Status
a   0        ecba68bfe604    2026-07-03T19:09:10Z  Inactive
b   1        29969f5f3e19    2026-07-03T19:29:32Z  Inactive
c   2        pluto.spei.ch   2026-07-03T19:47:12Z  Inactive
d   3        pluto-stalwart  2026-07-03T19:47:40Z  Inactive

The last is the correct one, but for some reason also stall (but it is running and working fine).