Email authentication through DKIM, SPF, and DMARC is no longer optional — major providers like Gmail and Outlook reject or junk messages from servers that lack these records. DKIM (DomainKeys Identified Mail) adds a cryptographic signature to outgoing messages, SPF (Sender Policy Framework) publishes a DNS record declaring which servers may send mail for your domain, and DMARC (Domain-based Message Authentication, Reporting and Conformance) tells receiving servers what to do when DKIM or SPF checks fail. This tutorial walks through implementing all three on a RHEL 9 server running Postfix, using OpenDKIM as the signing milter. Complete the Postfix tutorial before beginning.

Prerequisites

  • A RHEL 9 server running Postfix with a working outbound mail configuration
  • Authoritative control over the DNS zone for your domain (to add TXT records)
  • sudo or root access
  • bind-utils installed for testing with dig

Step 1 — Install OpenDKIM

sudo dnf install -y opendkim opendkim-tools

OpenDKIM runs as a milter (mail filter) that Postfix calls before delivering each outbound message. The signing daemon is opendkim and the key generation utility is opendkim-genkey.

Step 2 — Generate a DKIM Key Pair

Create a directory to store the key material, then generate a 2048-bit RSA key pair for your domain. The -s flag sets the selector name (a short label used to identify this key in DNS) and -d is your domain.

# Create key directory
sudo mkdir -p /etc/opendkim/keys/example.com

# Generate the key pair (selector name: "default")
sudo opendkim-genkey -b 2048 -s default -d example.com 
  -D /etc/opendkim/keys/example.com/

# Two files are created:
# default.private  — the private signing key
# default.txt      — the DNS TXT record to publish

# Set correct ownership
sudo chown -R opendkim:opendkim /etc/opendkim/keys/
sudo chmod 600 /etc/opendkim/keys/example.com/default.private

# View the DNS record you need to publish
cat /etc/opendkim/keys/example.com/default.txt

The default.txt file contains something like: default._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjAN..." )

Step 3 — Configure OpenDKIM

Edit the main OpenDKIM configuration file at /etc/opendkim.conf:

sudo vi /etc/opendkim.conf
Mode                    sv
PidFile                 /run/opendkim/opendkim.pid
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
UserID                  opendkim:opendkim
Socket                  inet:8891@localhost
Umask                   002
SendReports             yes
SoftwareHeader          yes

# Domain and key settings
Domain                  example.com
KeyFile                 /etc/opendkim/keys/example.com/default.private
Selector                default

# Trust localhost and Postfix internal network
InternalHosts           /etc/opendkim/TrustedHosts

KeyTable                /etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable

Create the supporting table files:

# TrustedHosts — IPs that are trusted to sign
sudo tee /etc/opendkim/TrustedHosts <<'EOF'
127.0.0.1
localhost
192.168.1.0/24
EOF

# KeyTable — maps key identifier to domain/selector/keyfile
sudo tee /etc/opendkim/KeyTable <<'EOF'
default._domainkey.example.com example.com:default:/etc/opendkim/keys/example.com/default.private
EOF

# SigningTable — maps senders to key identifiers
sudo tee /etc/opendkim/SigningTable <<'EOF'
*@example.com default._domainkey.example.com
EOF

sudo chown -R opendkim:opendkim /etc/opendkim/

Step 4 — Connect OpenDKIM to Postfix

Configure Postfix to pass all outgoing mail through the OpenDKIM milter. Add the milter settings to /etc/postfix/main.cf:

sudo postconf -e "milter_default_action = accept"
sudo postconf -e "milter_protocol = 6"
sudo postconf -e "smtpd_milters = inet:localhost:8891"
sudo postconf -e "non_smtpd_milters = inet:localhost:8891"

Start OpenDKIM, then reload Postfix:

sudo systemctl enable --now opendkim
sudo systemctl status opendkim
sudo systemctl reload postfix

Step 5 — Publish DNS Records for DKIM, SPF, and DMARC

In your DNS zone (either in BIND9’s zone file or your registrar’s control panel), add the following three TXT records. Substitute your server’s public IP for 203.0.113.10 and the actual base64 key from default.txt.

; DKIM record — selector "default", subdomain _domainkey
default._domainkey.example.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQ..."

; SPF record — authorise your mail server's IP to send for example.com
example.com.  IN  TXT  "v=spf1 ip4:203.0.113.10 include:_spf.google.com ~all"

; DMARC record — quarantine failures, send aggregate reports to [email protected]
_dmarc.example.com.  IN  TXT  "v=DMARC1; p=quarantine; pct=100; rua=mailto:[email protected]; ruf=mailto:[email protected]; fo=1"

If using BIND9, increment the zone serial number and reload: sudo systemctl reload named.

Step 6 — Test and Verify All Three Records

After DNS propagates (up to 48 hours for external DNS; immediate for local BIND9), verify each record with dig and send a test message to a checker service.

# Check DKIM record
dig TXT default._domainkey.example.com +short

# Check SPF record
dig TXT example.com +short | grep spf

# Check DMARC record
dig TXT _dmarc.example.com +short

# Send a test email (check headers in the received mail for DKIM-Signature)
echo "DKIM/SPF/DMARC test" | mail -s "Auth Test" [email protected]

# Check the mail log for DKIM signing
sudo grep opendkim /var/log/maillog | tail -20

For external verification, send a test message to [email protected] or use mxtoolbox.com/emailhealth to audit all three records simultaneously. A passing DKIM result shows dkim=pass in the Authentication-Results header of the received message.

Conclusion

You have installed OpenDKIM on RHEL 9, generated a 2048-bit RSA DKIM key pair, configured OpenDKIM to sign outgoing mail, connected it to Postfix via the milter interface, published the DKIM TXT record, an SPF record that authorises your server’s IP, and a DMARC policy that quarantines failing messages and sends forensic reports. Together these three mechanisms dramatically improve your domain’s email deliverability and protect recipients from spoofed messages. As your confidence grows, change the DMARC policy from p=quarantine to p=reject for the strictest level of protection.

Next steps: How to Install and Configure Postfix Mail Server on RHEL 9, How to Install and Configure Dovecot IMAP Server on RHEL 9, and How to Set Up a DNS Server with BIND9 on RHEL 9.