← MTA Administration Knowledge Base
Production MTA Operations

Executive Summary: Configure OpenDKIM with Postfix for multi-domain sending: managing KeyTable, SigningTable, TrustedHosts, 2048-bit RSA keys, and DNS publication.

Author: Fred Dawson (MTA Infrastructure Architect) Standard: AutoSMTP High-Volume SRE Category: MTA Administration Read Time: ~15 min
Key Length
2048-bit RSA
Cryptographic standard
Table Model
Dynamic Lookup
KeyTable & SigningTable
Selector Policy
Domain-Specific
Rotatable selectors
Verification
100% RFC 6376
Strict signature match

Configuring DKIM signing on a Postfix server for a single domain is well-documented. Extending that configuration to sign outbound mail for multiple separate domains — each with its own private key, its own selector, and its own DNS record — requires a different approach using OpenDKIM’s key table and signing table architecture. This guide covers the complete multi-domain OpenDKIM setup, the common milter permission issues that cause silent DKIM signing failures, key rotation procedures, and the verification steps that confirm each domain is signing correctly.

OpenDKIM
most common Postfix DKIM milter — open-source, well-documented
2048-bit
minimum RSA key — configure in opendkim.conf
milter
mail filter interface — Postfix connects milters via Unix socket or TCP
/etc/opendkim/keys/
default key storage path — protect with chmod 600

Architecture: KeyTable and SigningTable

Single-domain OpenDKIM configurations often use the simple KeyFile and Domain directives in opendkim.conf. This approach doesn’t scale to multiple domains because it maps all outbound mail to one key. The correct multi-domain architecture uses two lookup files:

  • KeyTable (/etc/opendkim/KeyTable): Maps a named key entry to a domain, selector, and private key file path
  • SigningTable (/etc/opendkim/SigningTable): Maps each sending domain (or email pattern) to a key entry from the KeyTable

When Postfix submits a message to the OpenDKIM milter, OpenDKIM reads the From: header domain, looks up that domain in the SigningTable, finds the corresponding key entry, and applies the correct private key for signing. Each domain gets its own key, its own selector, and generates its own DKIM signature.

Installation

# Debian/Ubuntu
sudo apt update
sudo apt install opendkim opendkim-tools -y

# RHEL/CentOS/Rocky Linux
sudo dnf install opendkim opendkim-tools -y

# Verify installation
opendkim --version

Directory Structure

Create a consistent directory layout before generating keys:

OpenDKIM + Postfix — complete multi-domain configuration
# /etc/opendkim.conf
Mode                    sv
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
Canonicalization        relaxed/relaxed
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
Mode                    sv
PidFile                 /var/run/opendkim/opendkim.pid
SignatureAlgorithm      rsa-sha256
Socket                  local:/run/opendkim/opendkim.sock

# /etc/opendkim/KeyTable (one line per domain)
default._domainkey.domain1.com domain1.com:default:/etc/opendkim/keys/domain1.com/default.private
default._domainkey.domain2.com domain2.com:default:/etc/opendkim/keys/domain2.com/default.private

# /etc/opendkim/SigningTable
*@domain1.com default._domainkey.domain1.com
*@domain2.com default._domainkey.domain2.com

# /etc/postfix/main.cf
milter_protocol = 6
milter_default_action = accept
smtpd_milters = local:/run/opendkim/opendkim.sock
non_smtpd_milters = local:/run/opendkim/opendkim.sock
sudo mkdir -p /etc/opendkim/keys
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 700 /etc/opendkim/keys

For each domain, keys will live in a per-domain subdirectory:

/etc/opendkim/keys/
    example.com/
        mail.private    # Private signing key (kept secret)
        mail.txt        # Public key for DNS (share this)
    anotherdomain.org/
        mail.private
        mail.txt

Generating 2048-bit Keys Per Domain

Generate a separate 2048-bit key for each domain. The selector name (mail in this example) appears in the DKIM-Signature header and in the DNS record name. Use date-based selectors for easier key rotation management (2026q2, 20260401):

# Create per-domain directory and generate key
sudo mkdir -p /etc/opendkim/keys/example.com
sudo opendkim-genkey 
  --directory=/etc/opendkim/keys/example.com 
  --domain=example.com 
  --selector=mail 
  --bits=2048

# Repeat for each additional domain
sudo mkdir -p /etc/opendkim/keys/anotherdomain.org
sudo opendkim-genkey 
  --directory=/etc/opendkim/keys/anotherdomain.org 
  --domain=anotherdomain.org 
  --selector=mail 
  --bits=2048

# Set correct ownership and permissions on all keys
sudo chown -R opendkim:opendkim /etc/opendkim/keys/
sudo chmod -R 600 /etc/opendkim/keys/*/*.private

2048-bit is mandatory. Gmail’s 2025 bulk sender guidance and Microsoft’s requirements both specify 2048-bit minimum key length. OpenDKIM’s default may be 1024-bit on older installations — always explicitly pass --bits=2048.

Configuring opendkim.conf

Replace the default OpenDKIM configuration at /etc/opendkim.conf:

# /etc/opendkim.conf — Multi-domain signing configuration

# Logging
Syslog          yes
SyslogSuccess   yes
LogWhy          yes

# Mode: s = sign, v = verify, sv = sign and verify
Mode            sv

# Canonicalization
# relaxed/simple is the standard recommendation
# relaxed allows minor header whitespace changes (safe for forwarding)
Canonicalization    relaxed/simple

# Socket — use TCP socket for Postfix compatibility
Socket          inet:8891@127.0.0.1

# Signing algorithm
SignatureAlgorithm  rsa-sha256

# User/Group for the OpenDKIM process
UserID          opendkim
UMask           002

# Multi-domain lookup files
KeyTable        refile:/etc/opendkim/KeyTable
SigningTable    refile:/etc/opendkim/SigningTable

# Trusted hosts (can inject unsigned mail)
ExternalIgnoreList  refile:/etc/opendkim/TrustedHosts
InternalHosts       refile:/etc/opendkim/TrustedHosts

# PID file
PidFile         /var/run/opendkim/opendkim.pid

KeyTable and SigningTable Files

KeyTable format

Each line maps a key name (arbitrary identifier) to domain:selector:private_key_path:

# /etc/opendkim/KeyTable
# Format: key-name    domain:selector:private-key-path
mail._domainkey.example.com     example.com:mail:/etc/opendkim/keys/example.com/mail.private
mail._domainkey.anotherdomain.org  anotherdomain.org:mail:/etc/opendkim/keys/anotherdomain.org/mail.private

SigningTable format

Maps email patterns or domains to key table entries. The refile: prefix in opendkim.conf enables regex matching:

# /etc/opendkim/SigningTable
# Format: pattern    key-name
*@example.com        mail._domainkey.example.com
*@anotherdomain.org  mail._domainkey.anotherdomain.org

# Subdomains — if you send from subdomain addresses
*@mail.example.com   mail._domainkey.example.com

TrustedHosts file

Lists hosts that can inject mail without requiring incoming DKIM verification:

# /etc/opendkim/TrustedHosts
127.0.0.1
::1
localhost
your.server.hostname.com

Set correct permissions:

sudo chown opendkim:opendkim /etc/opendkim/KeyTable 
  /etc/opendkim/SigningTable /etc/opendkim/TrustedHosts
sudo chmod 644 /etc/opendkim/KeyTable 
  /etc/opendkim/SigningTable /etc/opendkim/TrustedHosts

Postfix Milter Integration

Add the milter configuration to /etc/postfix/main.cf:

# /etc/postfix/main.cf — add to existing configuration
# OpenDKIM milter integration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters

The milter_default_action = accept setting is critical — it means that if OpenDKIM is unavailable or crashes, Postfix continues delivering email without DKIM signing rather than rejecting all outbound mail. In production, monitor OpenDKIM process health separately.

Restart both services:

sudo systemctl enable opendkim
sudo systemctl restart opendkim
sudo systemctl restart postfix

# Verify OpenDKIM is listening
ss -tlnp | grep 8891

The Permissions Problem: Most Common Failure

The most frequent cause of DKIM signing failures on multi-domain setups is a permissions mismatch between the OpenDKIM process user and the Postfix process group. OpenDKIM runs as the opendkim user; Postfix processes run under the postfix user. The milter socket communication works, but if the private key files aren’t readable by the OpenDKIM process, signing silently fails — mail is delivered without DKIM signatures and no error appears in the mail log.

# Add postfix user to opendkim group (allows socket access)
sudo usermod -a -G opendkim postfix

# Verify key file permissions — .private files must be readable by opendkim only
sudo ls -la /etc/opendkim/keys/example.com/
# Expected: -rw------- 1 opendkim opendkim mail.private
#           -rw-r--r-- 1 opendkim opendkim mail.txt

# Fix if wrong:
sudo chown -R opendkim:opendkim /etc/opendkim/keys/
sudo chmod 600 /etc/opendkim/keys/*/*.private
sudo chmod 644 /etc/opendkim/keys/*/*.txt

# Restart after group change
sudo systemctl restart postfix opendkim

Publishing DKIM DNS Records

Each domain needs a DNS TXT record. The public key is in the mail.txt file generated by opendkim-genkey:

# View the DNS record content
cat /etc/opendkim/keys/example.com/mail.txt

The output looks like:

mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
    "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ..."
    "...rest of key data..." )  ; ----- DKIM key mail for example.com

At your DNS provider, create a TXT record:

  • Host/Name: mail._domainkey (some providers require the full name: mail._domainkey.example.com)
  • Type: TXT
  • Value: v=DKIM1; k=rsa; p=YOURPUBLICKEY (the concatenated value without the parentheses and line breaks)

Verify DNS propagation:

dig TXT mail._domainkey.example.com +short

Verification and Testing

Test key configuration before sending

# Test that OpenDKIM can find and use each key
sudo -u opendkim opendkim-testkey 
  -d example.com 
  -s mail 
  -k /etc/opendkim/keys/example.com/mail.private 
  -v

A successful result shows: opendkim-testkey: using default configfile /etc/opendkim.conf followed by opendkim-testkey: key OK.

Send a test email and verify headers

Send email from each configured domain to a Gmail address you control. View the full message source (Gmail: three-dot menu → Show Original). Look for:

Authentication-Results: mx.google.com;
    dkim=pass header.i=@example.com header.s=mail header.b=AbCdEf;

If you see dkim=fail, check:

  1. Is the DNS record published and fully propagated? (use dig TXT mail._domainkey.example.com)
  2. Is the signing key in KeyTable matching the selector in the DNS record name?
  3. Are private key file permissions correct (readable by opendkim user)?
  4. Is the domain in the SigningTable matching the From: header exactly?

Check mail logs for DKIM activity

# Real-time log monitoring
sudo tail -f /var/log/mail.log | grep -i dkim

# Expected successful signing log entries:
# opendkim[PID]: MSGID: DKIM-Signature field added (s=mail, d=example.com)

Key Rotation for Multiple Domains

DKIM keys should be rotated every 90–180 days. The zero-downtime rotation process for multi-domain Postfix/OpenDKIM:

# Step 1: Generate new key with new selector name (e.g., date-based)
SELECTOR="mail2026q3"
DOMAIN="example.com"

sudo mkdir -p /etc/opendkim/keys/${DOMAIN}/${SELECTOR}
sudo opendkim-genkey 
  --directory=/etc/opendkim/keys/${DOMAIN}/${SELECTOR} 
  --domain=${DOMAIN} 
  --selector=${SELECTOR} 
  --bits=2048

# Step 2: Publish new DNS record
# Add TXT record for ${SELECTOR}._domainkey.example.com
# Wait 24-48 hours for DNS propagation

# Step 3: Add new key to KeyTable
echo "${SELECTOR}._domainkey.${DOMAIN}  ${DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DOMAIN}/${SELECTOR}/${SELECTOR}.private" 
  | sudo tee -a /etc/opendkim/KeyTable

# Step 4: Update SigningTable to use new key
sudo sed -i "s/mail._domainkey.${DOMAIN}/${SELECTOR}._domainkey.${DOMAIN}/" /etc/opendkim/SigningTable

# Step 5: Reload OpenDKIM
sudo systemctl reload opendkim

# Step 6: Verify new selector is signing (send test email, check headers)

# Step 7: After 30-day drain period, remove old DNS record
# Keep old private key file for any delayed message re-verification
📋 Client case — Email infrastructure team — Postfix serving 15 domains for a SaaS platform

Situation: DKIM signing silently failing on 3 of 15 domains. Authentication-Results showed dkim=neutral (no key for signature) for those domains.
Action taken: Root cause: KeyTable entries for the 3 domains had incorrect path separators. Corrected KeyTable. Restarted OpenDKIM. Tested with mail-tester.com for all 15 domains.
Outcome: DKIM pass rate: 100% across all domains. Verified by sending a test to each domain and checking Authentication-Results header in the received email.

▶ OpenDKIM multi-domain setup
1
Generate a 2048-bit key per domain — opendkim-genkey -s default -d domain1.com -b 2048. Outputs default.private and default.txt (DNS record content).
2
Publish DNS TXT record — Add the contents of default.txt to your DNS as: default._domainkey.domain1.com IN TXT “v=DKIM1; k=rsa; p=…”
3
Configure KeyTable and SigningTable — One line per domain in each file. KeyTable maps selector to key file. SigningTable maps email address pattern to selector.
4
Connect milter to Postfix — Add smtpd_milters and non_smtpd_milters to main.cf pointing to OpenDKIM socket. Restart both services.
5
Verify with mail-tester.com — Send a test email and check for dkim=pass in Authentication-Results. Confirm d= value matches the From: domain for DMARC alignment.

Once OpenDKIM is configured, rotate keys every six months following the DKIM key rotation guide. Pair this with a published DMARC policy to complete your authentication stack. For high-volume tuning of the Postfix instance itself, see Postfix high-volume configuration.

OpenDKIM Multi-Domain Configuration: The Complete Setup

Configuring OpenDKIM for multi-domain signing on Postfix requires three configuration files: the main OpenDKIM configuration (/etc/opendkim.conf), the signing table (/etc/opendkim/SigningTable), and the key table (/etc/opendkim/KeyTable). Understanding the role of each file prevents the most common multi-domain DKIM configuration errors.

The KeyTable maps a key identifier to the key file path, the domain, and the selector. Each signing domain needs an entry: key1._domainkey.brand1.com brand1.com:mail:/etc/opendkim/keys/brand1.com/mail.private. The three components after the key identifier are the domain (brand1.com), the selector (mail), and the private key file path. Generate a separate 2048-bit RSA key pair for each domain: opendkim-genkey -b 2048 -d brand1.com -s mail -D /etc/opendkim/keys/brand1.com/.

The SigningTable maps email addresses or domains to key identifiers from the KeyTable. To sign all email from brand1.com with the key1._domainkey.brand1.com signing key: *@brand1.com key1._domainkey.brand1.com. For wildcard matching to work, the SigningTable must use the refile: prefix in opendkim.conf: SigningTable refile:/etc/opendkim/SigningTable.

DNS record publication: for each domain, publish the DKIM public key TXT record at mail._domainkey.brand1.com. The public key content is in the .txt file generated by opendkim-genkey. Verify each domain’s DKIM configuration independently after setup: send a test message from Postfix using each domain’s From: address and review the Authentication-Results header to confirm dkim=pass header.d=brand1.com. A multi-domain DKIM configuration that signs correctly for domain A but fails for domain B is a common configuration error caused by a KeyTable-SigningTable mismatch — verify each domain independently rather than assuming all domains work if one works.

T
Tomasz Wiśniewski

MTA Administrator at AutoSMTP. Expert in PowerMTA and Postfix configuration, DKIM signing, queue management, and high-volume outbound operations.

Enterprise Mail Infrastructure

The AutoSMTP Bare-Metal MTA Fleet Advantage

AutoSMTP provisions and manages enterprise-grade PowerMTA and KumoMTA clusters on dedicated bare-metal servers. Zero shared IP cross-contamination, automated throttle modulation, and proactive ISP postmaster reputation management.

Turnkey Dedicated Bare-Metal Clusters

Pre-configured PowerMTA and KumoMTA installations on enterprise hardware with dedicated IPv4 subnets and predictable flat pricing.

Automated Gas-Pedal IP Warming

Dynamic ramp-up algorithms that modulate hourly dispatch volume against real-time deferrals and Google Postmaster spam ratios.

Native FBL & Socket Interceptors

Automated RFC 3464 bounce ingestion, Yahoo/Outlook CFL complaint webhooks, and instant in-band suppression sync.

Full Cryptographic Alignment

2048-bit DKIM key rotation, strict DMARC enforcement (p=reject), FCrDNS PTR verification, and BIMI readiness.

Ready for Dedicated Sending Infrastructure?

Eliminate shared pool throttle penalties and take complete control over your mail delivery with custom-tuned MTA clusters.

Deploy Dedicated MTA Fleet →