Issue Description
When an email arrives with an RCPT using a domain alias, it’s randomly treated as non local, and sent to my outbound relay instead of being delivered localy. It’s not systematic (sometimes, it’s correctly delivered localy) so I suspect a cache issue a bit like Fix Directory: Mail addressed to a domain alias is rejected with `550… · stalwartlabs/stalwart@deb034b · GitHub
This is running stalwart 0.16.19
Expected Behavior
Email using a domain alias should always be treated as local
Actual Behavior
The email is first accepted, and placed in the “local” queue. Then the stalwart instance responsible for outbound delivery treats it like remote, and tries to deliver it to my outbound relay. Not sure if running stalwart as a distributed install has an impact (I have only 2 kinds of nodes : 1 for outbound delivery, and 1 for everything else. Using valkey as coordinator)
The problem exist after a cold start of every components. But once an email is delivered to the main domain, after that, emails to the domain alias start working
Reproduction Steps
- Configure a domain with an alias
- Send an email to a valid recipient, but using the domain alias
- The mail is accepted by the inbound SMTP, and placed in the local queue
- The email in the local queue is delivered as if it was remote (eg, to an outbound relay) instead of being delivered localy
Relevant Log Output
In these logs snippets, [email protected] is an external address trying to send an email to [email protected]. dani is a valid account on the domain foo.org, which has alias.foo.org as a domain alias configured
First, we can see the email being accepted and placed in the local queue (I just removed the DNSBL lookup logs as they are not relevent)
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO SMTP EHLO command (smtp.ehlo) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, domain = "pmg.foo.org"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO SPF EHLO check failed (smtp.spf-ehlo-fail) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, domain = "pmg.foo.org", result = No SPF record (spf.none), elapsed = 0ms
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z DEBUG SMTP STARTTLS command (smtp.start-tls) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z WARN Multiple TLS certificates available (tls.multiple-certificates-available) total = 5
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO TLS handshake (tls.handshake) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, listenerId = "smtp", version = "TLSv1_3", details = "TLS13_AES_256_GCM_SHA384"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO IPREV check passed (smtp.iprev-pass) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, domain = "pmg.foo.org", result = IPREV check passed (iprev.pass) { details = ["pmg.foo.org."] }, elapsed = 1ms
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO SPF From check failed (smtp.spf-from-fail) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, domain = "pmg.foo.org", from = "[email protected]", result = SPF check failed (spf.fail), elapsed = 2ms
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO SMTP MAIL FROM command (smtp.mail-from) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, from = "[email protected]"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z DEBUG Cache hit (store.cache-hit) key = "alias.foo.org", collection = "domainName"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z DEBUG Cache hit (store.cache-hit) key = 4, collection = "domainId"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z DEBUG Cache hit (store.cache-hit) key = "dani", domain = 4, collection = "email"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z DEBUG Cache hit (store.cache-hit) key = 66, collection = "account"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO SMTP RCPT TO command (smtp.rcpt-to) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, to = "[email protected]"
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO DKIM verification passed (smtp.dkim-pass) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, strict = false, result = [DKIM verification passed (dkim.pass) { domain = "acme.com" }], elapsed = 0ms
2026-08-25T10:32:19 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:19Z INFO DMARC check passed (smtp.dmarc-pass) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, strict = false, domain = "acme.com", policy = "quarantine", result = DMARC check passed (dmarc.pass), elapsed = 9ms
[...]
2026-08-25T10:32:20 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:20Z DEBUG Cache miss (store.cache-miss) key = "acme.com", collection = "domainName"
[...]
2026-08-25T10:32:20 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:20Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
[...]
2026-08-25T10:32:21 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:21Z DEBUG Cache miss (store.cache-miss) key = "pmg.foo.org", collection = "domainName"
[...]
2026-08-25T10:32:22 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:22Z DEBUG Cache miss (store.cache-miss) key = "zmstore.acme.com", collection = "domainName"
[...]
2026-08-25T10:32:22 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:22Z DEBUG Cache miss (store.cache-miss) key = "zmproxy.acme.com", collection = "domainName"
[...]
2026-08-25T10:32:22 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:22Z DEBUG Cache miss (store.cache-miss) key = "pmg.acme.com", collection = "domainName"
[...]
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache miss (store.cache-miss) key = "linkedin.com", collection = "domainName"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache miss (store.cache-miss) key = "goo.gl", collection = "domainName"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Sieve runtime error (sieve.runtime-error) listenerId = "smtp", localPort = 25, remoteIp = 10.99.2.14, remotePort = 59924, reason = "error sending request for url (https://goo.gl/maps/euyoe1ypqcy29tzy8)", details = "Failed to send request"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "linkedin.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
2026-08-25T10:32:23 [j=stalwart, g=backend, t=stalwart, i=0] 2026-08-25T08:32:23Z DEBUG Cache hit (store.cache-hit) key = "acme.com", collection = "domainNameNegative"
[...]
Then, my node dedicated to outbound delivery (which STALWART_ROLE only has the “Outbound Email MTA” task) takes it from the local queue, and tries to deliver it remotely to my outbound relay, instead of delivering it localy (note that my outbound relay isn’t working here, and this is on purpose as I’m working on a test system)
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO Delivery attempt started (delivery.attempt-start) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z DEBUG Cache miss (store.cache-miss) key = "alias.foo.org", collection = "domainName"
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO New delivery attempt for domain (delivery.domain-delivery-start) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, domain = "alias.foo.org"
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z DEBUG IP address lookup (delivery.ip-lookup) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, domain = "alias.foo.org", hostname = "127.0.0.1", details = [127.0.0.1], limit = 10, elapsed = 0ms
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO TLSA record not DNSSEC signed (dane.tlsa-record-not-dnssec-signed) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, domain = "alias.foo.org", hostname = "127.0.0.1", strict = false, elapsed = 0ms
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO Connecting to remote server (delivery.connect) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, domain = "alias.foo.org", hostname = "127.0.0.1", localIp = 0.0.0.0, remoteIp = 127.0.0.1, remotePort = 2525, elapsed = 0ms
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO SMTP greeting failed (delivery.greeting-failed) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, domain = "alias.foo.org", hostname = "127.0.0.1", details = "Temporary Failure for 127.0.0.1: Connection failed: Unparseable SMTP reply"
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z DEBUG Cache hit (store.cache-hit) key = "alias.foo.org", collection = "domainNameNegative"
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO Message rescheduled for delivery (queue.rescheduled) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, nextRetry = 2026-08-25T08:34:29Z, nextDsn = 2026-08-26T08:32:29Z, expires = 2026-08-28T08:32:29Z
2026-08-25T10:32:29 [j=stalwart-sender, g=sender, t=stalwart, i=0] 2026-08-25T08:32:29Z INFO Delivery attempt ended (delivery.attempt-end) queueId = 325827668083736576, queueName = "local", from = "[email protected]", to = ["[email protected]"], size = 12485, total = 1, elapsed = 10ms
Note that the final failure of the outbound relay (“Connection failed: Unparseable SMTP reply”). I’ve blocked the connection as this is a test system. But stalwart shouldn’t try to talk to this relay at all, and just deliver this email localy
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
PostgreSQL
Blob Storage
PostgreSQL
Search Engine
PostgreSQL
Directory Backend
OIDC
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