How to Properly Configure SPF, DKIM, and DMARC for Your Mail Server (Postfix + Gmail Deliverability)

Gmail has strict requirements for accepting bulk email. If you’re running your own mail server (e.g. Postfix), follow this guide to configure everything according to Google's standards and improve your deliverability.

πŸ”§ 1. DNS Setup (A, PTR, SPF, DKIM, DMARC)

  • A-record: Ensure each mail hostname resolves to the correct IP.
  • PTR (rDNS): The IP address must reverse-resolve to the same domain as your HELO/EHLO hostname.
  • SPF: Example record:
    vindazo.be. IN TXT "v=spf1 ip4:144.76.216.108 ip4:144.76.216.109 ip4:148.251.233.101 -all"
  • DKIM: Generate a key using opendkim-genkey and publish it as a TXT record:
    alert._domainkey.vindazo.be. IN TXT "v=DKIM1; k=rsa; p=MIIBI...QAB"
  • DMARC: Start with:
    _dmarc.vindazo.be. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@vindazo.be; fo=1"

πŸ“„ 2. Postfix and OpenDKIM Setup

Configure Postfix to use OpenDKIM:

milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:12301
non_smtpd_milters = inet:localhost:12301

In /etc/opendkim.conf:

Domain             vindazo.be
KeyFile            /etc/opendkim/keys/vindazo.be/alert.private
Selector           alert
Canonicalization   relaxed/relaxed
Mode               sv

πŸ” 3. Testing Results from SpamAssassin

Here’s a real-world test score example:

DKIM_VALID          0.1  ✅ DKIM is valid
SPF_PASS            0.001 ✅ SPF matches sender
SPF_HELO_NONE      -0.001 ⚠️ HELO domain missing SPF
RCVD_IN_RP_SAFE     2.0 ✅ Whitelisted relay
T_REMOTE_IMAGE     -0.01 ⚠️ External images present
  

To fix SPF_HELO_NONE, publish this record for the HELO domain (e.g. alert.vindazo.be):

alert.vindazo.be. IN TXT "v=spf1 ip4:144.76.216.109 -all"

πŸ“¬ 4. DKIM Validation Result

Example valid DKIM signature:


v=1; a=rsa-sha256; c=simple/simple; d=vindazo.be; s=alert;
bh=EnX/...; h=Subject:From:To:Date;
b=j7zdLO3Qk5R8C/oqBSEjY...
  

Validated using:

πŸ” 5. Recommended DMARC Policy Progression

  1. Start with:
    v=DMARC1; p=none; rua=mailto:dmarc@vindazo.be; fo=1
  2. After verifying reports, move to:
    v=DMARC1; p=quarantine; pct=50; rua=mailto:dmarc@vindazo.be; fo=1
  3. Eventually:
    v=DMARC1; p=reject; rua=mailto:dmarc@vindazo.be; fo=1

πŸ”§ 6. Useful Tools

With SPF, DKIM, and DMARC correctly configured, and PTR/HELO alignment in place, your mail server is fully compliant with Gmail's bulk sender guidelines. Always test changes using live Gmail inboxes or mail-tester, and monitor your DMARC reports regularly.

Google Requirments
FAQ

Need help automating this setup for multiple servers? Use monitoring tools, scripts, or managed platforms to simplify operations.

✅ Configure Postfix and OpenDKIM with Multiple Domains and IPs

This guide walks you through configuring Postfix and OpenDKIM for multiple domains using separate IP addresses and HELO names.

πŸ“Œ Domains and IP Mapping

DomainIPHELO Hostname
example1.com192.0.2.10mail.example1.com
example2.net192.0.2.20mail.example2.net
example3.org192.0.2.30mail.example3.org

1️⃣ Install Required Packages

apt update
apt install postfix opendkim opendkim-tools

2️⃣ OpenDKIM Configuration

/etc/opendkim.conf

Syslog                  yes
UMask                   002
Socket                  inet:8891@localhost
UserID                  opendkim:opendkim
PidFile                 /var/run/opendkim/opendkim.pid
Mode                    sv
Canonicalization        relaxed/relaxed
OversignHeaders         From
TrustAnchorFile         /usr/share/dns/root.key
DNSTimeout              5
SignatureAlgorithm      rsa-sha256

KeyTable                /etc/opendkim/KeyTable
SigningTable            /etc/opendkim/SigningTable
ExternalIgnoreList      /etc/opendkim/TrustedHosts
InternalHosts           /etc/opendkim/TrustedHosts

/etc/opendkim/TrustedHosts

127.0.0.1
localhost
192.0.2.10
192.0.2.20
192.0.2.30

/etc/opendkim/SigningTable

info@example1.com     default._domainkey.example1.com
info@example2.net     default._domainkey.example2.net
info@example3.org     default._domainkey.example3.org

/etc/opendkim/KeyTable

default._domainkey.example1.com example1.com:default:/etc/opendkim/keys/example1.com/default.private
default._domainkey.example2.net example2.net:default:/etc/opendkim/keys/example2.net/default.private
default._domainkey.example3.org example3.org:default:/etc/opendkim/keys/example3.org/default.private

Generate DKIM Keys

mkdir -p /etc/opendkim/keys/example1.com
opendkim-genkey -D /etc/opendkim/keys/example1.com/ -d example1.com -s default

3️⃣ Postfix Configuration

/etc/postfix/main.cf

milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

sender_dependent_default_transport_maps = regexp:/etc/postfix/sdd_transport_maps.regexp

/etc/postfix/sdd_transport_maps.regexp

/info@example1\.com$/  example1_transport
/info@example2\.net$/  example2_transport
/info@example3\.org$/  example3_transport

/etc/postfix/master.cf

example1_transport unix - - n - - smtp
  -o smtp_bind_address=192.0.2.10
  -o smtp_helo_name=mail.example1.com

example2_transport unix - - n - - smtp
  -o smtp_bind_address=192.0.2.20
  -o smtp_helo_name=mail.example2.net

example3_transport unix - - n - - smtp
  -o smtp_bind_address=192.0.2.30
  -o smtp_helo_name=mail.example3.org

4️⃣ Restart Services

systemctl restart opendkim
systemctl restart postfix

5️⃣ Testing

Use opendkim-testkey and swaks to verify configuration:

opendkim-testkey -d example1.com -s default -vvv
swaks --from info@example1.com --to test@example.net --server 127.0.0.1

✅ Final Checklist

  • DKIM key published in DNS
  • Matching SigningTable and KeyTable
  • Correct file permissions
  • Postfix master.cf contains right IPs and HELOs
  • SPF & DMARC configured correctly

Swaks Test Script

#!/bin/bash

# Variables
FROM="info@mydomain.nl"
TO="test-9op06vehp@srv1.mail-tester.com"
HELO="mail.mydomain.nl"
SERVER="127.0.0.1"
SUBJECT="DKIM/SPF Test via Swaks"
BODY="This is a test message sent from Swaks to check DKIM, SPF and HELO."

# Send email via Swaks
swaks --from "$FROM" \
      --to "$TO" \
      --server "$SERVER" \
      --data "Subject: $SUBJECT\n\n$BODY" \
      --h-Subject "$SUBJECT" \
      --header "From: $FROM" \
      --header "To: $TO" \
      --ehlo "$HELO"

echo "Test message sent to $TO"
  

Comments