DNS & Email Setup
Configure DNS records, DKIM signing, SPF, DMARC, and bounce handling for reliable email delivery.
For production email delivery, your self-hosted Owlat instance needs proper DNS configuration. Without it, most email providers will reject or spam-folder your messages.
DNS setup is only needed for production. For local testing, the MTA will attempt delivery with the default settings — most emails will be rejected by receiving servers, but you can verify the pipeline works end-to-end.
Required DNS Records
A Record
Point your application domain to your server's IP address:
owlat.example.com. A 203.0.113.10
PTR / Reverse DNS
The EHLO_HOSTNAME in your .env must have a PTR record matching your server's IP address. This is set through your hosting provider's control panel (not your DNS provider).
# Your .env
EHLO_HOSTNAME=mail.example.com
# PTR record (set in hosting provider)
203.0.113.10 PTR mail.example.com
Most receiving mail servers reject connections where the EHLO hostname doesn't match the PTR record.
MX Record for Bounce Processing
The RETURN_PATH_DOMAIN needs an MX record pointing to your server so bounce emails are routed back to the MTA on port 25:
bounces.example.com. MX 10 mail.example.com.
SPF
Add a TXT record on your sending domain authorizing your server's IP to send email:
example.com. TXT "v=spf1 ip4:203.0.113.10 -all"
If you use multiple IPs (separate transactional and campaign pools), include all of them:
example.com. TXT "v=spf1 ip4:203.0.113.10 ip4:203.0.113.11 -all"
DKIM
DKIM signs outgoing emails with a cryptographic key, allowing receivers to verify the message hasn't been tampered with.
1. Generate a Key Pair
# Generate private key
openssl genrsa -out dkim-private.pem 2048
# Extract public key
openssl rsa -in dkim-private.pem -pubout -outform PEM -out dkim-public.pem
2. Add the DNS TXT Record
Extract the base64 content from dkim-public.pem (strip the headers and join into one line):
# Extract just the base64 content
grep -v '^-' dkim-public.pem | tr -d '\n'
Create a TXT record at s1._domainkey.example.com:
s1._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."
If your public key is longer than 255 characters, you may need to split it across multiple quoted strings in the TXT record. Most DNS providers handle this automatically.
3. Configure the DKIM_KEYS Environment Variable
Read the private key file and format it as a single-line string with \n for newlines:
# Convert private key to single-line format
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' dkim-private.pem
Set the DKIM_KEYS variable in your .env as a JSON object:
DKIM_KEYS={"example.com":{"selector":"s1","privateKey":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAK...\n-----END RSA PRIVATE KEY-----\n"}}
The JSON format supports multiple domains:
{
"example.com": {
"selector": "s1",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
},
"anotherdomain.com": {
"selector": "s1",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
}
}
DMARC
DMARC tells receiving servers what to do when SPF or DKIM checks fail. Add a TXT record at _dmarc.example.com:
_dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com"
| Policy | Behavior |
|---|---|
p=none | Monitor only — no action on failures (good for initial setup) |
p=quarantine | Send failing messages to spam folder |
p=reject | Reject failing messages entirely (strictest) |
Start with p=none while verifying your setup, then move to p=quarantine or p=reject once everything is working.
IP Pools
The MTA supports separate IP pools for transactional and campaign emails. This protects your transactional email reputation from being affected by marketing campaigns.
# In .env
IP_POOLS_TRANSACTIONAL=203.0.113.10
IP_POOLS_CAMPAIGN=203.0.113.11,203.0.113.12
Each IP in a pool needs its own PTR record and must be included in your SPF record.
If you only have one IP address, use it for both pools. Reputation separation won't apply, but everything will work correctly.
IP Warming
Fresh IP addresses have no sending reputation. Sending large volumes immediately will trigger spam filters and potentially get your IP blocklisted. Gradually increase volume over 2-4 weeks.
A typical warming schedule for a new IP:
| Week | Daily Volume |
|---|---|
| 1 | 50-100 emails |
| 2 | 500-1,000 emails |
| 3 | 5,000-10,000 emails |
| 4+ | Full volume |
Focus on sending to engaged recipients first (recent openers/clickers) to build positive reputation signals.
Verification
Once your DNS records are configured:
- In-app verification — Owlat's domain verification flow checks SPF, DKIM, and DMARC records automatically. See Deliverability for details.
- External testing — send a test email to mail-tester.com and aim for a score of 9+/10.
- Manual checks:
# Verify SPF dig TXT example.com +short # Verify DKIM dig TXT s1._domainkey.example.com +short # Verify DMARC dig TXT _dmarc.example.com +short # Verify PTR dig -x 203.0.113.10 +short
For details on the MTA architecture, rate limiting, and bounce processing, see MTA System.