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.

Local development

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..."
DNS TXT record length

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"
PolicyBehavior
p=noneMonitor only — no action on failures (good for initial setup)
p=quarantineSend failing messages to spam folder
p=rejectReject 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.

Single IP setup

If you only have one IP address, use it for both pools. Reputation separation won't apply, but everything will work correctly.

IP Warming

New IPs have no reputation

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:

WeekDaily Volume
150-100 emails
2500-1,000 emails
35,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:

  1. In-app verification — Owlat's domain verification flow checks SPF, DKIM, and DMARC records automatically. See Deliverability for details.
  2. External testing — send a test email to mail-tester.com and aim for a score of 9+/10.
  3. 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.