Contents
DKIM (DomainKeys Identified Mail) signing is a required component of any production email infrastructure. Gmail's 2024 bulk sender requirements mandate DKIM for all senders over 5,000 daily messages. Microsoft enforces DMARC which relies on DKIM alignment. Yahoo requires DKIM for FBL configuration enrollment. Beyond these mandatory requirements, DKIM is one of the core signals that ISPs use to build sender reputation — messages signed with a consistent key from a domain with clean reputation receive preferential treatment over unsigned messages from the same IP.
How DKIM Signing Works in PowerMTA
PowerMTA performs DKIM signing at message acceptance time — when a message is injected via SMTP and accepted into the spool. The signing operation adds a DKIM-Signature header to the message before it enters the delivery queue. The private key used for signing is stored on disk; the corresponding public key is published in DNS and used by receiving MTAs to verify the signature.
# DKIM flow: # 1. Message injected into PowerMTA via SMTP # 2. PowerMTA reads private key from /etc/pmta/dkim/yourdomain.private # 3. Computes cryptographic hash of specified headers + message body # 4. Signs hash with private key → DKIM-Signature header added to message # 5. Message enters delivery queue with DKIM-Signature attached # 6. Receiving MTA retrieves public key from DNS: s1._domainkey.yourdomain.com # 7. Verifies signature — result: dkim=pass or dkim=fail in Authentication-Results # The signing domain must match the From: domain for DMARC alignment configuration # from: noreply@yourdomain.com + d=yourdomain.com in DKIM-Signature = aligned
DKIM Key Generation
# Generate 2048-bit RSA DKIM key (recommended standard) mkdir -p /etc/pmta/dkim openssl genrsa -out /etc/pmta/dkim/yourdomain.private 2048 # Extract public key for DNS publication openssl rsa -in /etc/pmta/dkim/yourdomain.private -pubout -out /etc/pmta/dkim/yourdomain.public # Format public key for DNS TXT record # Remove the header/footer lines and newlines: grep -v "^-" /etc/pmta/dkim/yourdomain.public | tr -d '\n' # Set correct permissions — private key must be readable by pmta user chmod 640 /etc/pmta/dkim/yourdomain.private chown root:pmta /etc/pmta/dkim/yourdomain.private # Verify key can be read pmta check-dkim --domain=yourdomain.com --selector=s1 # Should return: DKIM key OK — 2048 bits
DNS TXT Record Publication
# DNS TXT record format for DKIM public key # Record name: [selector]._domainkey.[domain] # Example: s1._domainkey.yourdomain.com # TXT record value format: "v=DKIM1; k=rsa; p=[BASE64_PUBLIC_KEY]" # Full example DNS record: s1._domainkey.yourdomain.com. 3600 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." # Verify DNS publication: dig TXT s1._domainkey.yourdomain.com # Should return the TXT record with your public key # Test DKIM via external tool (mxtoolbox.com/dkim.aspx) # Or send a test message and check the Authentication-Results header: # Authentication-Results: mx.google.com; dkim=pass header.d=yourdomain.com
PowerMTA DKIM Configuration
# DKIM configuration in virtual-mta block (signs all messages from this virtual MTA) <virtual MTA pool configuration-ip-1> smtp-source-host 185.x.x.10 mail1.yourdomain.com dkim-sign domain="yourdomain.com" \ key-file="/etc/pmta/dkim/yourdomain.private" \ selector="s1" \ header-list="From:To:Subject:Date:Message-ID:Content-Type:MIME-Version" # Domain-level override (useful if different domains need different keys)# Override DKIM for Gmail — use Gmail-specific selector if desired dkim-sign domain="yourdomain.com" \ key-file="/etc/pmta/dkim/yourdomain.private" \ selector="s1" # header-list — specify which headers are included in the DKIM signature # Minimum: From:To:Subject:Date:Message-ID # Recommended extended: From:To:Subject:Date:Message-ID:Content-Type:MIME-Version:Reply-To
Multi-Domain and Multi-Key Configuration
# Different DKIM key per brand / sending domainsmtp-source-host 185.x.x.10 mail1.brand-a.com dkim-sign domain="brand-a.com" \ key-file="/etc/pmta/dkim/brand-a.private" \ selector="s1" smtp-source-host 185.x.x.20 mail1.brand-b.com dkim-sign domain="brand-b.com" \ key-file="/etc/pmta/dkim/brand-b.private" \ selector="s1" # Subdomain signing (sending from notifications.yourdomain.com)smtp-source-host 185.x.x.30 notif1.yourdomain.com dkim-sign domain="notifications.yourdomain.com" \ key-file="/etc/pmta/dkim/notifications-yourdomain.private" \ selector="n1" # DNS record: n1._domainkey.notifications.yourdomain.com TXT "v=DKIM1; k=rsa; p=..." # DMARC alignment: From: domain must match signing domain # For From: alerts@notifications.yourdomain.com → d=notifications.yourdomain.com
Verifying DKIM Signatures in Production
# Method 1: Check Authentication-Results header in received messages # Send a test to a Gmail account and inspect the raw headers: # Authentication-Results: mx.google.com; # dkim=pass header.i=@yourdomain.com header.s=s1 header.b=AbCdEfGh # Method 2: Check PowerMTA accounting log format # DKIM result appears in the accounting log dkim field grep "dkim" /var/log/pmta/accounting-YYYYMMDD.csv | head -5 # Method 3: Use MXToolbox DKIM validator # https://mxtoolbox.com/dkim.aspx # Enter: domain=yourdomain.com, selector=s1 # Common DKIM failures: # dkim=fail (body hash mismatch) — message body modified in transit # dkim=fail (signature expired) — sending time wrong / signature-expiry too short # dkim=temperror — DNS lookup failure for public key record # dkim=none — signing not configured or signature header missing
DKIM key rotation Without Downtime
# Key rotation procedure — zero-downtime approach # Step 1: Generate new key with new selector openssl genrsa -out /etc/pmta/dkim/yourdomain-s2.private 2048 chmod 640 /etc/pmta/dkim/yourdomain-s2.private && chown root:pmta /etc/pmta/dkim/yourdomain-s2.private # Step 2: Publish new public key in DNS # s2._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=[NEW_PUBLIC_KEY]" # Wait for DNS propagation (24-48 hours) # Step 3: Update PowerMTA config to use new selectordkim-sign domain="yourdomain.com" \ key-file="/etc/pmta/dkim/yourdomain-s2.private" \ selector="s2" # Changed from s1 to s2 pmta reload # Apply new signing configuration # Step 4: Keep old s1 DNS record for 7 days # Messages signed with s1 during the transition are still valid # After 7 days: remove s1._domainkey.yourdomain.com DNS record # After 30 days: delete old private key file
Frequently Asked Questions
Operating PowerMTA at production volume?
We manage PowerMTA environments for high-volume senders — configuration, IP warming schedule, daily reputation monitoring, and operational response. Fully managed. No self-service.

