DNS Setup for HIN Gateway¶
This guide covers all DNS records required for a working HIN Gateway deployment. Configure these records before installing HIN Gateway or immediately after, depending on the record type.
Throughout this guide:
<HIN Gateway_IP>- your HIN Gateway server's public static IP address (SERVER_STATIC_IPincustomer-config.sh)<MAIL_HOSTNAME>- the FQDN of the HIN Gateway relay (e.g.mail.example.ch; configured via the dashboard's/mailpage)<YOUR_DOMAIN>- your mail domain (e.g.example.ch; configured via the dashboard's/mailpage)
Record Summary¶
| Record | Name | Value | Required | When |
|---|---|---|---|---|
| A | <MAIL_HOSTNAME> | <HIN Gateway_IP> | Yes | Before install |
| MX | <YOUR_DOMAIN> | <MAIL_HOSTNAME> (priority 15) | Yes | Before install |
| SPF | <YOUR_DOMAIN> | ip4:<HIN Gateway_IP> added to TXT | Yes | Before install |
| PTR | <HIN Gateway_IP> | <MAIL_HOSTNAME> | Recommended | Before install |
| DMARC | _dmarc.<YOUR_DOMAIN> | v=DMARC1; p=none; ... | Recommended | After install |
| DKIM | selector._domainkey.<YOUR_DOMAIN> | From M365/provider | Recommended | After install |
For multi-domain deployments, repeat the MX, SPF, DMARC, and DKIM records for each domain listed in MAIL_DOMAINS.
Required Records¶
A Record¶
Create an A record pointing the HIN Gateway mail hostname to the server's public IP:
Example:
If the HIN Gateway has an IPv6 address, add an AAAA record as well:
Why: External mail servers connect to this hostname to deliver mail. Without the A record, the MX record below is unresolvable.
MX Records¶
Add an MX record for the HIN Gateway with a higher priority (lower number) than the existing mail server. This ensures inbound mail reaches the HIN Gateway first for S/MIME processing before being forwarded to Exchange or your mail platform.
Keep the existing Exchange / mail server MX record at a lower priority (higher number):
Example (complete MX set):
Info
The lower MX number means higher priority. HIN Gateway at priority 15 receives mail before Exchange Online at priority 20.
Why: The HIN Gateway intercepts inbound mail, processes S/MIME, then forwards to the next MX (Exchange). The second MX record is also used by Stalwart to know where to relay processed mail.
Important: If the HIN Gateway is the only MX record for a domain, Stalwart will filter out its own hostname and have no delivery target. Always keep a second MX pointing to your actual mail server.
SPF Record¶
Add the HIN Gateway server IP and the HIN sealer IP to your domain's SPF record so outbound mail relayed through it passes SPF checks at the recipient's end.
If you use M365 / Exchange Online:
<YOUR_DOMAIN>. TXT "v=spf1 ip4:<HIN Gateway_IP> ip4:<HIN_SEALER_IP> include:spf.protection.outlook.com -all"
If you do not use M365 / Google Workspace:
Example:
example.ch. TXT "v=spf1 ip4:128.140.117.200 ip4:193.247.208.66 include:spf.protection.outlook.com -all"
Why the HIN sealer IP is required
When the HIN Gateway produces a SEAL'd (encrypted) message for a non-HIN recipient, the final outbound hop to the recipient is the HIN sealer, not your HIN Gateway or M365. Without the sealer IP in your SPF record, every SEAL'd outbound message will fail SPF at the recipient and - because there is no DKIM signature on the SEAL'd payload - DMARC will fail too. Strict-DMARC recipients (Gmail, Outlook with p=reject enforcement, Proofpoint) will reject or junk the message.
Sealer IPs to add in SPF:
HIN publishes more than one sealer host (e.g. mx1, mx2, mx3), include all their IPs. Resolve them with dig +short mx hin.ch followed by dig +short A <each-mx>. Until you have the full list, leave the SPF policy at ~all (softfail) instead of -all (hardfail) so that legitimate SEAL mail through any unlisted sealer IP is not outright rejected.
SPF lookup limit
The total include: chain in an SPF record must stay under 10 DNS lookups. Adding ip4: entries does not count toward this limit. Check your count with MXToolbox SPF lookup.
How the HIN Gateway uses SPF: The mtaconf daemon resolves each domain's SPF record to auto-populate the list of IPs allowed to relay through the HIN Gateway without authentication. This is how Microsoft 365 outbound IPs get whitelisted automatically - they appear in the include:spf.protection.outlook.com chain.
Recommended Records¶
PTR (Reverse DNS)¶
Configure the reverse DNS (PTR) record for the HIN Gateway IP to match <MAIL_HOSTNAME>:
This is configured at your hosting provider (Hetzner, Azure, AWS, etc.), not in your domain registrar's DNS panel. Most providers have a "Reverse DNS" or "rDNS" setting in the server/IP management page.
Why: Many receiving mail servers (including Gmail and Outlook) check that the connecting IP's PTR record resolves to a hostname, and that hostname resolves back to the same IP (forward-confirmed reverse DNS / FCrDNS). A missing or mismatched PTR is a strong spam signal and can cause delivery failures.
DMARC Record¶
Publish a DMARC policy for each sending domain. Start with p=none (monitoring only), then tighten after confirming alignment:
Example:
Once DMARC aggregate reports confirm that SPF and/or DKIM pass consistently, tighten the policy:
p=none- monitoring only (start here)p=quarantine- suspicious mail goes to spamp=reject- unauthorized mail is rejected
Why: DMARC ties SPF and DKIM together and tells recipients what to do with mail that fails both. Even p=none is enough to clear Outlook's "we can't verify this sender" banner, as long as SPF passes.
Check your DMARC record: MXToolbox DMARC lookup
DKIM Records¶
If your domain is an accepted domain in M365 or Google Workspace, enable DKIM signing in the admin centre and publish the CNAME records as instructed:
M365 example:
selector1._domainkey.<YOUR_DOMAIN>. CNAME selector1-<YOUR_DOMAIN_DASHED>._domainkey.<TENANT>.onmicrosoft.com.
selector2._domainkey.<YOUR_DOMAIN>. CNAME selector2-<YOUR_DOMAIN_DASHED>._domainkey.<TENANT>.onmicrosoft.com.
Note
Publishing the CNAME records alone is not enough - DKIM signing must also be enabled in the M365 admin centre (Defender portal > Email authentication > DKIM).
Why: DKIM proves the message body was not tampered with in transit. When combined with SPF and DMARC, it provides the strongest sender authentication.
Multi-Domain Setup¶
For deployments handling multiple mail domains (configured via the dashboard's /mail page), each domain needs its own set of DNS records.
Per-Domain Records¶
For each configured domain:
| Record | Required |
|---|---|
MX pointing to <MAIL_HOSTNAME> | Yes |
SPF including ip4:<HIN Gateway_IP> | Yes |
DMARC (_dmarc.<domain>) | Recommended |
| DKIM (from your mail provider) | Recommended |
The A record and PTR record are shared (they point to the HIN Gateway server, not to individual domains).
Per-Domain Mail Routing¶
Each domain's MX records tell the HIN Gateway where to deliver processed mail. If different domains use different Exchange servers:
domain1.ch MX 15 mail.domain1.ch.
domain1.ch MX 20 exchange1.domain1.ch.
domain2.ch MX 15 mail.domain2.ch.
domain2.ch MX 20 exchange2.domain2.ch.
Alternatively, configure explicit per-domain relay targets via the dashboard's /mail page (relay host field per domain) to override MX-based routing.
Verification¶
After configuring all records, verify them:
# A record
host <MAIL_HOSTNAME>
# Expected: <MAIL_HOSTNAME> has address <HIN Gateway_IP>
# MX records
host -t mx <YOUR_DOMAIN>
# Expected: Both HIN Gateway and Exchange MX records listed
# SPF record
host -t txt <YOUR_DOMAIN> | grep v=spf1
# Expected: SPF record includes ip4:<HIN Gateway_IP>
# PTR (reverse DNS)
host <HIN Gateway_IP>
# Expected: <HIN Gateway_IP> → <MAIL_HOSTNAME>
# Forward-confirmed reverse DNS (FCrDNS)
host $(host <HIN Gateway_IP> | awk '{print $NF}' | sed 's/\.$//')
# Expected: resolves back to <HIN Gateway_IP>
# DMARC
host -t txt _dmarc.<YOUR_DOMAIN>
# Expected: v=DMARC1; p=...
# DKIM (M365)
host -t cname selector1._domainkey.<YOUR_DOMAIN>
# Expected: CNAME to your tenant's onmicrosoft.com
Example output:
$ host mail.example.ch
mail.example.ch has address 128.140.117.200
$ host -t mx example.ch
example.ch mail is handled by 15 mail.example.ch.
example.ch mail is handled by 20 example-ch.mail.protection.outlook.com.
$ host -t txt example.ch | grep v=spf1
example.ch descriptive text "v=spf1 ip4:128.140.117.200 include:spf.protection.outlook.com -all"
$ host 128.140.117.200
200.117.140.128.in-addr.arpa domain name pointer mail.example.ch.
$ host -t txt _dmarc.example.ch
_dmarc.example.ch descriptive text "v=DMARC1; p=none; rua=mailto:postmaster@example.ch"
Online tools:
- MXToolbox MX Lookup
- MXToolbox SPF Check (includes lookup count)
- MXToolbox DMARC Check
- Mail-Tester (send a test mail to get a deliverability score)
Troubleshooting¶
"Client host rejected: Access denied" (554 5.7.1)¶
Stalwart is rejecting the sending server because its IP is not in the allowed relay list. This usually means:
- The SPF record for your domain does not include the sending server's IP range
- The mail configuration has not been reloaded since the SPF record was updated
Reload the mail configuration via the dashboard's /mail page (submit the config again), or restart the container: docker compose restart stalwart
Mail flagged as spam / "can't verify sender"¶
- SPF is missing or does not include the HIN Gateway IP - add
ip4:<HIN Gateway_IP>to your SPF record - DMARC is not published - add at least
v=DMARC1; p=none - PTR record is missing or mismatched - configure reverse DNS at your hosting provider
- DKIM is not enabled in your M365/provider tenant
MX lookup returns only the HIN Gateway¶
If the HIN Gateway is the only MX for a domain, Stalwart filters out its own hostname and has no relay target. Add a second MX record pointing to your mail server:
example.ch. MX 15 mail.example.ch. ← HIN Gateway (inbound)
example.ch. MX 20 example-ch.mail.protection.outlook.com. ← Exchange (relay target)
SPF lookup count exceeded (> 10)¶
Each include: in the SPF record triggers additional DNS lookups. The total chain must stay under 10. Solutions:
- Use
ip4:/ip6:entries instead ofinclude:where possible (they do not count) - Flatten nested includes using a tool like SPF Flattener
- Remove unused
include:entries from old providers
Port 25 blocked by hosting provider¶
Some cloud providers (Azure, certain Hetzner plans) block outbound port 25 by default. Check with your provider and request an exception. This affects both inbound delivery (external servers connecting to your HIN Gateway) and outbound relay (HIN Gateway delivering to MX targets).