@Mail Domain Check

OpenDKIM | Postfix | Docker SMTP

DKIM Record Exists, but Authentication-Results Says dkim=none

A public key does not sign a message. Prove which Postfix path accepted one fresh message, whether OpenDKIM classified that client as internal, and whether the resulting message actually contains a DKIM-Signature.

Fastest unsigned-message check

Start with the exact published-key, missing-signature and dkim=none path before changing DNS again.

The public scan is free. A complete prioritized report is 1 USDC on Base and is delivered automatically without an account or email address.

Decision: separate key publication from message signing

Published selector
A TXT or CNAME result proves that a verifier can retrieve a candidate public key. It does not prove the sending MTA used the matching private key.
DKIM-Signature header
This is the evidence that a signer acted on that message. If the header is absent, changing DNS alone cannot make the already-sent message pass.
Receiver result
dkim=none, dkim=fail, and dkim=pass are different boundaries. Use the topmost trusted receiver result from the same fresh message.

Do not post a private DKIM key, complete configuration, full message header, recipient address, message body, container environment, or raw mail log. The planner uses categorical states only. The optional scan form sends only a public domain.

Browser-local evidence planner

Find the first unsigned boundary

No domain, selector, address, message, configuration, key, container value, or log is requested or transmitted by this planner.

Why Docker SMTP can stay unsigned

OpenDKIM documents InternalHosts as the set whose mail is signed instead of verified. Replacing the default list means every internal source that should be signed must be represented explicitly.

Postfix applies smtpd_milters to mail received by smtpd. It applies non_smtpd_milters to local sendmail or queue-injection paths. A message submitted by an application container over SMTP therefore needs the SMTP Milter path and a source identity that OpenDKIM treats as internal.

Trust only the exact application subnet or authenticated submission path you control. Adding all of 172.16.0.0/12 merely because Docker commonly uses that range widens the signing boundary unnecessarily.

One bounded verification

  1. Identify the selector from the intended signer configuration or a fresh DKIM-Signature. Confirm the exact public key with opendkim-testkey or the public selector checker.
  2. Classify how the application handed the message to Postfix: SMTP through smtpd, or local sendmail/cleanup.
  3. For SMTP submission, compare the application's observed source address or exact subnet with OpenDKIM InternalHosts. For local submission, verify non_smtpd_milters.
  4. Send one fresh message and inspect only the new message. First require a DKIM-Signature with the intended d= and s=; then require the trusted receiver to report dkim=pass.
postconf smtpd_milters non_smtpd_milters milter_default_action
opendkim-testkey -d example.com -s selector -vvv

Keep the command output private. A successful key lookup proves DNS and key matching; it does not prove that a particular message traversed the signer.

If dkim=pass but the message still lands in spam

Stop changing DKIM once the fresh message passes with the intended aligned signing domain. Continue with DMARC policy and alignment, forward-confirmed PTR and HELO identity, sending-IP reputation, content, complaint history, volume, and receiver-specific evidence. A monitoring policy such as p=none does not request quarantine or rejection, and dkim=pass does not guarantee inbox placement.

Run the public domain, SPF, DMARC, DKIM-selector, PTR and policy checks before changing multiple boundaries at once.

Public case that exposes the boundary

A public project reported receiver evidence with SPF and DMARC passing while DKIM was absent. Its later repository configuration publishes a selector and configures both Postfix Milter paths, but the generated OpenDKIM trusted-host list contains loopback and the sending domain while the application submits over a Docker private address. The public key can therefore exist while the application source still needs to be proven inside the signing boundary.

This is a source-based hypothesis, not proof of the current runtime. A fresh message and protected runtime evidence decide it.

Primary sources