NAME
dkim2-milter - Standalone DKIM2 milter for Postfix
SYNOPSIS
# Verify only (no signing)
dkim2-milter --socket unix:/var/run/dkim2.sock
# Sign for a single domain
dkim2-milter --socket unix:/var/run/dkim2.sock \
--domain example.com \
--selector sel1 \
--keyfile /etc/dkim2/sel1.pem
# Sign for any domain with keys in a directory
dkim2-milter --socket unix:/var/run/dkim2.sock \
--keydir /etc/dkim2/keys
# With message snapshots for diff-based Message-Instance
dkim2-milter --socket unix:/var/run/dkim2.sock \
--keydir /etc/dkim2/keys \
--snapshot-dir /var/spool/dkim2/snapshots
# Epilogue-based MI (body in MIME epilogue, compact header)
dkim2-milter --socket unix:/var/run/dkim2.sock \
--keydir /etc/dkim2/keys \
--snapshot-dir /var/spool/dkim2/snapshots \
--use-epilogue
# Auto-switch to epilogue when diff exceeds 5 literal lines
dkim2-milter --socket unix:/var/run/dkim2.sock \
--keydir /etc/dkim2/keys \
--snapshot-dir /var/spool/dkim2/snapshots \
--epilogue-threshold 5
# Testing with dns.json instead of real DNS
dkim2-milter --socket inet:8891@localhost \
--keydir keys \
--dns-json dns.json
DESCRIPTION
A standalone milter daemon that provides DKIM2 signature verification and signing for Postfix (or any milter-compatible MTA). Uses Sendmail::PMilter for the milter protocol.
Verification is enabled by default for all inbound messages.
Signing can be configured in two ways:
- Single domain:
--domain,--selector, and--keyfile. Signs only when the envelope sender matches the configured domain. - Key directory:
--keydir PATH. The directory contains subdirectories named by domain, each containing key files namedselector.key. The milter looks up the sender domain (with parent domain fallback) and uses the first key found. The algorithm is auto-detected from the key type (RSA or Ed25519).
This program implements draft-ietf-dkim-dkim2-spec-06; see "STATUS" in Mail::DKIM2.
KEY DIRECTORY LAYOUT
The --keydir option points to a directory tree where each subdirectory is named after a domain and contains one or more PEM key files with a .key extension. The filename (minus the extension) becomes the DKIM selector.
/etc/dkim2/keys/
example.com/
sel1.key # RSA key, selector "sel1"
sel2.key # another key (used if newer)
sub.example.com/
default.key # selector "default"
other.org/
ed25519.key # Ed25519 key, auto-detected
Domain lookup
When a message arrives from user@sub.example.com, the milter:
Looks for a directory named
sub.example.com/under the keydir.If not found, strips the leftmost label and tries
example.com/.Continues stripping labels until a match is found or no labels remain.
The signing domain (d= tag) is set to the directory name that matched, not necessarily the full sender domain.
Key selection
If a domain directory contains multiple .key files, the newest file by ctime is used. This makes key rotation straightforward:
# Generate a new key and publish its DNS record
openssl genrsa -out /etc/dkim2/keys/example.com/sel2.key 2048
# The milter will start using sel2 for new signatures immediately
# (after the domain lookup cache is refreshed on next restart).
# Keep sel1.key around until its DNS record has propagated.
Algorithm detection
The signing algorithm is auto-detected from each key file:
Ed25519 keys (32-byte raw or PEM-wrapped) use the
ed25519algorithm.All other keys (RSA) use
rsa-sha256.
No --algorithm flag is needed in keydir mode.
Caching
Domain-to-key mappings are cached in memory for the lifetime of the milter process. After adding or removing key files, restart the milter to pick up the changes.
OPTIONS
- --socket, -s SPEC
-
Milter socket specification. Default:
unix:/var/run/dkim2-milter.sock. Examples:unix:/path/to/sock,inet:8891@localhost. - --domain, -d DOMAIN
-
Signing domain (single-domain mode). Requires
--selectorand--keyfile. - --selector SELECTOR
-
DKIM selector (single-domain mode). Requires
--domainand--keyfile. - --keyfile, -k PATH
-
Path to a PEM-encoded private key (single-domain mode).
- --keydir PATH
-
Directory of per-domain key subdirectories (multi-domain mode). Cannot be combined with
--domain/--selector/--keyfile. - --algorithm, -a ALG
-
Signing algorithm for single-domain mode. Default:
rsa-sha256. In keydir mode, the algorithm is auto-detected from each key file. - --snapshot-dir PATH
-
Directory for message snapshots. Enables diff-based Message-Instance computation: on inbound, a snapshot is stored keyed by the MI header value; on outbound, the snapshot is used to compute recipes describing changes.
- --use-epilogue
-
When computing a diff-based MI, always store the previous message body in the MIME epilogue rather than as inline diff lines. This keeps MI headers small regardless of how much the body changed, at the cost of increasing the transmitted message size. Cannot be combined with
--epilogue-threshold. - --epilogue-threshold N
-
When computing a diff-based MI, switch to epilogue storage only when the diff recipe would contain more than N literal (non-range) lines. Small changes (
<= Nlines) stay as a compact inline diff; large changes fall back to epilogue.5is a reasonable starting value. Cannot be combined with--use-epilogue. - --dns-json PATH
-
Path to a JSON file with DNS key records (for testing without real DNS). Format:
{ "domain": { "selector._domainkey": [["txt", "v=DKIM1; ..."]] } }. - --verify, --no-verify
-
Enable or disable verification. Default: enabled.
- --sign, --no-sign
-
Enable or disable signing. Default: enabled if signing config is available.
- --help, -h
-
Show this help message.
POSTFIX CONFIGURATION
Add to main.cf:
# For inbound verification
smtpd_milters = unix:/var/run/dkim2-milter.sock
# For outbound signing (locally-generated mail)
non_smtpd_milters = unix:/var/run/dkim2-milter.sock
# Milter default action if the milter is unavailable
milter_default_action = accept
DEPENDENCIES
Requires Sendmail::PMilter 1.28 or later, a recommended (not required) dependency of the Mail-DKIM2 distribution:
cpanm Sendmail::PMilter
1.28 (July 2026) is the first release that answers a null-sender MAIL FROM:<>; with 1.27 the MTA waited out its milter timeout and bounces went out unsigned. This program refuses to start with an older version. deploy/smoke-null-sender-milter.pl in the interop repository checks a running milter answers a null sender.
AUTHOR
Bron Gondwana <brong@fastmailteam.com>
COPYRIGHT AND LICENSE
Copyright (c) 2025 Fastmail Pty Ltd. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.