550 SPF Permanent Error: Two or more type TXT spf records found
Receiving mail servers may reject or distrust outgoing mail due to SPF PermError.
Expected Behavior
The generated SPF configuration should merge mechanisms into a single TXT record, for example:
"v=spf1 a mx -all"
Actual Behavior
"v=spf1 a -all"
"v=spf1 mx -all"
It is being generated for the domain zone file.
Reproduction Steps
I do not know how to reproduce it but this is only happening for one domain: mail.example.com is my mail server domain and a mail send/receive domain. Stalwart generates two SPF records in the zone file. notifications.example.com is another one, which only has "v=spf1 mx -all".
Also, example.com is configured for another mail provider.
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
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.
Let me look into how Stalwart generates SPF records for DNS zone files.
This is a confirmed bug in the SPF record generation code. Looking at the DnsRecordType::Spf branch in crates/common/src/network/dns/records.rs, the logic is:
For each MX hostname that is a subdomain of the domain (e.g. mail.example.com), it emits: TXT mail.example.com. "v=spf1 a -all"
Then unconditionally for the domain itself, it emits: TXT example.com. "v=spf1 mx -all"
The issue you’re hitting is specifically when mail.example.com is also configured as a send/receive domain in Stalwart. In that case, when the SPF records are generated for mail.example.com as a domain, it gets both records at the same name:
TXT mail.example.com. "v=spf1 a -all" (from the MX subdomain logic, because mail.example.com ends with .example.com)
TXT mail.example.com. "v=spf1 mx -all" (from the unconditional domain record)
This violates RFC 7208 which allows exactly one v=spf1 TXT record per name. The fix would need to either merge the mechanisms into a single record (v=spf1 a mx -all) or skip the a sub-record when the MX hostname equals the domain itself.
Workaround until this is fixed: Manually manage the DNS records for mail.example.com instead of using Stalwart’s auto-generated zone. Publish a single merged record:
TXT mail.example.com. "v=spf1 a mx -all"
This is a bug that should be reported to the development team – a community member or maintainer will follow up.
This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong. If you found it useful, mention @helpbot in any reply to ask follow-up questions and the bot will respond again.