Blog · Dmarc

Why Docker Mail Server DMARC Alignment Fails: How to Fix It

A Docker mail stack can pass both SPF and DKIM authentication and still fail DMARC alignment. The aggregate report shows it as one row, same source IP, with header_from=example.com, dkimdomain=docker.local, and spfdomain=mail.22.203.in-addr.arpa. Authentication is not alignment. DMARC requires whichever mechanism passes to also produce an identifier that matches the visible From: domain. The failure is invisible until you actually pull the report, which is exactly the kind of work DMARCFlow is built to do: it ingests aggregate reports and surfaces the source IPs where the From domain and the dkim or spf domain disagree.

This post walks through the three container-specific failure modes that break DMARC alignment, the diagnostic flow that finds which one is broken, and the order to fix them.

What DMARC alignment actually checks

Per RFC 7489 Section 3, DMARC passes when either SPF or DKIM produces a "pass" result AND the identifier in that result aligns with the domain in the visible From: header. The two checks are independent: a message can pass SPF authentication but fail SPF alignment if the envelope sender domain does not match the From: domain, and the same message can pass DKIM authentication but fail DKIM alignment if the d= in the DKIM signature does not match the From: domain. Authentication is not alignment.

Container deployments break alignment in three specific ways that vanilla DMARC explainers do not cover.

Failure mode 1: HELO hostname leaks the container ID

When Postfix runs inside a container, its default myhostname is often the container ID or the Compose service name. The EHLO greeting announces something like mail.postfix.example.com or svc-mail.6f7e1a1a2b3c, depending on how the image was built. SPF sees that hostname, runs the lookup against the A or PTR record for it, and produces an authentication result. If that hostname does not belong to the same domain as the visible From:, SPF alignment fails.

The fix is to set Postfix myhostname to a fully qualified name under the From domain:

bash
postconf -e 'myhostname = mail.example.com'
postconf -e 'smtpd_banner = $myhostname ESMTP'
postfix reload

For a Compose setup, set it in the environment and restart only the mail service:

yaml
environment:
  - POSTFIX_MYHOSTNAME=mail.example.com

Then send a test message and confirm the EHLO banner now reports mail.example.com and not the container ID. This is the single most common cause of container mail servers failing DMARC alignment. DMARCFlow stops flagging the source IP for that misalignment in the next aggregate report.

Failure mode 2: DKIM signs the wrong domain

OpenDKIM reads its KeyTable and SigningTable at startup. If the image was built before the correct d= was configured, every signed message still uses the old d=. Authentication passes, alignment fails.

The fix is to rebuild the signing table with the correct d= for the From domain, then signal the daemon to reload without restarting the whole container:

bash
docker exec mail opendkim-testkey -d example.com -s default
docker exec mail kill -HUP $(docker exec mail pgrep opendkim)

The kill -HUP form is portable across distributions. After the reload, send a test message and check the Authentication-Results header on the receiving side. Confirm the DKIM signature shows d=example.com and not the stale d=docker.local or whatever the original build had. DMARCFlow ingests the aggregate report and groups the rows by source IP, so the broken DKIM d= alignment shows up as a cluster pointing back to your egress.

Failure mode 3: relayhost envelope rewriting

When Postfix relays outbound mail through an upstream SMTP (SendGrid, Mailgun, Amazon SES), the upstream rewrites the envelope sender to its own bounce domain. SPF now authenticates the upstream's sending IP against its own SPF record, which does not align with the From domain. DKIM may still align if the upstream re-signs with the correct d=, but most major providers support custom DKIM only when you configure the right selector on their side.

The fix is to configure the upstream to preserve DKIM signing for your domain. For SES, that means using the SES-managed DKIM signing configured for example.com. For SendGrid or Mailgun, it is their branded-send APIs or whitelabel DKIM. If your provider does not support custom DKIM, route through a different provider for mail that must pass DMARC alignment.

The reverse DNS trap

PTR records for the container host often point to a docker bridge or the host machine name, not the sending domain. RFC 7489 only checks SPF and DKIM identifiers, so PTR is not strictly required for DMARC alignment. But missing PTR alignment breaks trust scoring at major receivers and triggers spam placement independent of DMARC. Set the PTR record at your provider to match the sending domain. Confirm with dig -x and host from an external vantage point.

Verification checklist

Pull the latest DMARC aggregate report from the rua endpoint. The report is XML. Look for rows where the source IP matches your container egress IP. For each row, compare:

  • to for DKIM alignment
  • to for SPF alignment

If either does not match, the row is the failure. DMARCFlow groups these rows by source IP, so a misalignment that affects many recipients shows up as one cluster. The fix flow is: identify the row, map it to one of the three failure modes above, fix only that piece, reload or redeploy, then wait for the next aggregate report. After each fix, wait for the next aggregate report before declaring victory.

When it is not a Docker problem

If the alignment failures do not match your container's egress IP, the failure is not in the container. Check upstream relay providers, third-party senders, and any service that uses your domain in the From header. The aggregate report rows identify which source IP sent the failing message, which tells you whose infrastructure to look at.

Fix order

Start with the easiest: confirm Postfix myhostname matches the From domain. Then rebuild the OpenDKIM signing table for the correct d=. Then address any relayhost that rewrites the envelope sender. Run a test message through Gmail or Outlook after each fix. After each fix, wait for the next aggregate report before declaring victory.

DMARCFlow watches aggregate reports as they arrive. When the next report shows your container egress IP aligning on both SPF and DKIM, this post's problem is closed. Until then, the report is the only way to confirm a fix actually took.

FAQ

What is DMARC alignment in plain English?

DMARC requires that whichever mechanism (SPF or DKIM) produces a passing authentication result must also produce an identifier that matches the visible From: domain. Authentication alone is not enough.

Why does my container mail server fail DMARC when SPF and DKIM both pass?

The mechanism passes authentication but the identifier it produces does not match the From domain. Common container causes are HELO hostname leakage, stale DKIM signing tables baked into the image, and relayhost envelope rewriting.

How do I find which row in the aggregate report is the failure?

Look for rows where the source IP matches your container egress IP. Compare to and . If either does not match, the row is misaligned.

Does DMARCFlow fix the alignment?

No. DMARCFlow ingests the aggregate reports and surfaces the rows where alignment fails. The fix is in Postfix myhostname, the OpenDKIM signing table, and the relayhost configuration.

Do I need to restart the container to apply Postfix myhostname changes?

No. postconf -e followed by postfix reload applies the change inside the running container. For Compose, set the value in the environment and restart only the mail service.

What if the alignment failure is from a relayhost?

Configure the relayhost provider to preserve DKIM signing for your domain (custom DKIM, branded send, whitelabel). If the provider does not support it, route through a different provider for mail that must pass DMARC alignment.