PhoenixDKIM is a standalone DKIM signing and verification milter, descended from trusteddomainproject/OpenDKIM, with a focus on security and correctness.
It reads OpenDKIM-style key and signing tables, so migration is straightforward (see the Removed section below for what is no longer accepted).
Documentation and guides live on the website: https://www.phoenixdkim.org/documentation.html. That is their authoritative, rendered home; the Markdown sources under
docs/are what it is built from.
- Quick Start
- Highlights
- Removed from pre-fork OpenDKIM 2.11.0-beta2
- Packages and Ports
- Dependencies
- Building
- Configuration
- Key Generation
- Platforms
- Documentation
- Known Runtime Issues
- Origin
- Licence
- Legal notice
- Bugs and Contributions
New to PhoenixDKIM and want to sign your first message? Follow docs/quickstart.md — generate an Ed25519 and an RSA key, publish two DNS records, write a six-line config, wire it to Postfix, and verify the result. The rest of this README is reference material you can dip into as needed.
- OpenSSL 3+ — all cryptography ported to the EVP high-level API
- Ed25519 — signing and verification per RFC 8463
- LMDB — replaces BerkeleyDB for key storage
- CMake — replaces autoconf/automake
- Lua 5.4+ — updated from Lua 5.1
- Dynamic key backends —
http:/https:and HashiCorp Vault (vault:) data sets, plus Redis, alongside flat files and LMDB. The native SQL and LDAP drivers are gone; point the HTTP backend at a small bridge to reach those stores (optional,-DWITH_CURL=ON) - Zero-downtime key rotation — a Vault secret can list several
currently-valid selectors, and PhoenixDKIM signs with all of them at once (old
- new, RSA + Ed25519) across the overlap window, so a rollover needs no flag day. The secret layout matches Rspamd's, so the same store works in both
- RSA-SHA1 signing removed; an RSA-SHA1 signature is never treated as valid on
verification (reported
dkim=neutral, neverdkim=pass) per RFC 8301 §3.1, withOn-WeakAlgorithmchoosing only the message disposition - Minimum RSA signing key size: 2048 bits (a deliberate choice — RFC 8301 permits 1024)
- DNSSEC-aware key verification — a passing signature whose key record is
not DNSSEC-protected can be downgraded or rejected (
UnprotectedKey), a control most DKIM implementations don't expose. It works with the stock validating resolver via the reply's AD bit; no libunbound required. Crucially, an absent AD bit is treated as ambiguous rather than "insecure": before penalising a signature the filter runs a Postfix-styleDNSSECProbeto confirm the resolver actually validates, logsdkim=policy/neutraldecisions and "DNSSEC validation may be unavailable" warnings, and suppresses the penalty when validation can't be confirmed — so a non-validating resolver never silently fails every message - Reproducible builds — both the source tarball and the Debian package
produce bit-for-bit identical artifacts regardless of build path or time
(
-ffile-prefix-map,-Wdate-time, deterministic archives by default;reproducible=+allon the.deb)
Removed: VBR, ATPS, RBL, reputation subsystems, LDAP, SQL/OpenDBX,
GnuTLS, BerkeleyDB (and its QUERY_CACHE DNS-result cache),
diffheaders (tre dependency).
See docs/removed-features.md for the
rationale behind each.
Currently available: .deb and .rpm
See: Packages and Ports on www.phoenixdkim.org.
To build the library and filter you will need:
- A C17-capable compiler (GCC 8+ or Clang 5+)
- CMake >= 3.20
- OpenSSL >= 3.0, or LibreSSL >= 3.7 (e.g. OpenBSD base)
- LMDB
- libmilter (from Sendmail or as a standalone package)
- libresolv
- libidn2 — RFC 8616 U-label (internationalized) signing-domain resolution
(default ON;
-DWITH_IDN=OFFto build without)
Optional:
- Lua 5.4+ — policy scripting hooks (default ON)
- libunbound — DNSSEC-aware DNS resolution (
-DWITH_UNBOUND=ON) - libcurl >= 7.20.0 — SMTP report delivery via the
SMTPURIconfig option (-DWITH_CURL=ON) - hiredis or libvalkey — Redis/Valkey signing-table backend (
-DWITH_REDIS=ON) - libsystemd —
Type=notifyreadiness signalling and watchdog (-DWITH_SYSTEMD; auto-detected on Linux) - libbsd — provides
strlcpy/strlcat- only on systems without them (not needed on glibc 2.38+, FreeBSD, or OpenBSD)
apt install build-essential cmake libssl-dev liblmdb-dev \
libmilter-dev liblua5.4-dev libidn2-dev libsystemd-dev
# Only on Debian 12 or lower, or Ubuntu 23.04 or lower
apt install libbsd-dev
# optional
apt install libcurl4-openssl-dev libhiredis-dev
dnf install gcc cmake openssl-devel lmdb-devel sendmail-devel \
lua-devel libidn2-devel systemd-devel
# Only on Fedora 37 or lower, or EPEL 9 or lower
dnf install libbsd-devel
# optional
dnf install libcurl-devel hiredis-devel
pkg install cmake openssl lmdb milter lua54 libidn2
# optional
pkg install curl hiredis
LibreSSL and strlcpy are in the base system — no crypto or BSD-compat package needed.
pkg_add git cmake lmdb libmilter lua%5.4 libidn2
# optional
pkg_add unbound libevent
CMake automatically searches /usr/local on OpenBSD, so no extra -D flags are
needed for milter. A minimal build:
cmake -B build -DSSL_PROVIDER=libressl
cmake --build build -j$(sysctl -n hw.ncpuonline)
ctest --test-dir build
cmake -B build
cmake --build build
ctest --test-dir build
Common build options:
| Option | Default | Description |
|---|---|---|
-DSSL_PROVIDER=auto |
auto | Crypto provider: auto (system default — OpenSSL on Linux, LibreSSL on OpenBSD), openssl, or libressl. Non-auto values validate that the located library is that provider |
-DSSL_ROOT_DIR=<prefix> |
(unset) | Build against a non-default OpenSSL/LibreSSL install. Handy for compat testing against, say, a side-installed OpenSSL 4 or a specific LibreSSL: -DSSL_PROVIDER=openssl -DSSL_ROOT_DIR=/opt/openssl-4 |
-DWITH_LUA=ON |
ON | Enable Lua 5.4 policy scripting |
-DWITH_UNBOUND=ON |
ON | Enable libunbound DNSSEC resolver |
-DWITH_CURL=ON |
OFF | Enable libcurl SMTP report delivery (SMTPURI) |
-DWITH_REDIS=ON |
OFF | Enable Redis/Valkey signing-table backend |
-DWITH_SYSTEMD=AUTO |
AUTO | systemd sd_notify readiness/watchdog: AUTO detects, ON requires it (configure fails if libsystemd is absent), OFF disables |
-DINSTALL_SYSTEMD_UNIT=ON |
ON on Linux | Install the generated phoenixdkim.service (its Type=/WatchdogSec match the build) |
-DCMAKE_BUILD_TYPE=Release |
RelWithDebInfo |
Build type (Debug/Release/RelWithDebInfo/MinSizeRel) |
To install:
cmake --install build
See phoenixdkim.conf(5) for the full list of configuration options.
A sample configuration file, which needs editing, is installed at
/usr/share/doc/phoenixdkim/phoenixdkim.conf.sample.
For an example on using multiple signatures per e-mail (e.g. Ed25519 with
an RSA key as fall-back), see docs/multisigning.md.
For a step-by-step procedure to rotate signing keys without a verification
gap, see docs/key-rotation.md.
The filter integrates with Postfix and Sendmail via the milter protocol.
For Postfix, add to main.cf:
smtpd_milters = unix:/run/phoenixdkim/phoenixdkim.sock
non_smtpd_milters = unix:/run/phoenixdkim/phoenixdkim.sock
milter_default_action = accept (or: tempfail)
The systemd service unit is generated from contrib/systemd/phoenixdkim.service.in
at build time (so its Type=/WatchdogSec match what was compiled) and
installed by make install; the Debian package ships its own unit in debian/.
Note: Run the following in the directory where you want your keys, e.g.
/etc/dkimkeys/domain.com/, or move the files after generating them.
Generate an RSA-2048 or Ed25519 signing keypair with phoenixdkim-genkey:
# RSA
phoenixdkim-genkey -b 2048 -d domain.com -s selector
# Ed25519
phoenixdkim-genkey -t ed25519 -d domain.com -s selector
- Linux (glibc 2.17+)
- FreeBSD 13+
- OpenBSD 7+
The guides are published on the website at https://www.phoenixdkim.org/documentation.html; their Markdown sources are in docs/ and cover getting started (quickstart), key rotation, multi-signing, crypto policy, and metrics & observability.
Man pages are installed for phoenixdkim(8), phoenixdkim.conf(5),
phoenixdkim-genkey(8), phoenixdkim-genzone(8), phoenixdkim-testkey(8),
phoenixdkim-testmsg(8), and phoenixdkim-lua(3).
RFC and draft reference documents are in the docs/ directory.
The filter attempted to read an MTA macro that the MTA did not provide.
For Postfix, ensure the relevant macros are configured. For Sendmail,
regenerate sendmail.cf from your M4 configuration.
DNS queries for key records may exceed the default MTA milter timeout.
Increase milter_timeout in Postfix, or the milter timeout in Sendmail
if you encounter this.
A public key record was retrieved but could not be decoded. Possible causes: memory exhaustion, or a corrupted or malformed key record in DNS. If using tempfail mode the sender will retry; a repeated failure indicates a problem with the published key.
If you use Sendmail's MASQUERADE_AS or FEATURE(genericstable),
phoenixdkim signs headers before Sendmail rewrites them. The verifying
side will see rewritten headers that do not match the signature.
Solutions: disable the rewriting features, use a two-MTA setup where
the signing MTA does no rewriting, or use multiple DaemonPortOptions
entries to separate rewriting from signing.
Note that as per May 2026 it seems that OpenDKIM is getting updates again, currently in the 'develop' branch. Perhaps a new release is in the making!
OpenDKIM had seen no meaningful upstream development for years, yet remained widely deployed. Rather than abandon it or switch to an alternative, I decided to fork it — to continue using a tool I relied on, updated and hardened to the standard an internet-facing daemon handling email deserves.
The work focused on what matters for a long-lived, security-sensitive daemon:
- Removed obsolete and unmaintained subsystems (VBR, ATPS, RBL, LDAP, GnuTLS, BerkeleyDB, and others) — less code means fewer vulnerabilities and an easier codebase to reason about
- Updated the cryptography: ported to the OpenSSL 3 EVP API, added Ed25519 signing and verification
- Replaced the autoconf/automake build system with CMake
- Updated the systemd unit files to current best practices
- Hardened the build (compiler warnings, sanitisers, security flags) and fixed the bugs they exposed
- Security-audited the daemon and library code, with assistance from AI tooling
The project was originally named opendkim-ng. It was renamed to PhoenixDKIM to avoid confusion with the upstream trusteddomainproject/OpenDKIM project, which after a long period of inactivity has indicated plans to resume active development.
The licence for this package is in the LICENSE file. Portions of
the code originating from Sendmail are covered by the Sendmail Open
Source Licence, found in LICENSE.Sendmail. See the copyright notice
in each source file for which licence(s) apply.
A number of legal regimes restrict the use or export of cryptography. If you are potentially subject to such restrictions you should seek legal advice before using, developing, or distributing cryptographic code.
Please report bugs and submit contributions via GitHub: