DKIM Signing With OpenDKIM on a Small MTA
DKIM survives forwarding when SPF does not. Sign with OpenDKIM on Postfix, publish a 2048-bit TXT key, and verify the signature on a received copy.
Netbay Cloud Team
Netbay Engineering
On this page
DKIM attaches a cryptographic signature to headers and a hash of the body. The receiving MX fetches your public key from DNS and checks the signature. Unlike SPF, the check does not depend on the connecting IP, so forwarding and mailing lists do not automatically break it. If you run a small Postfix on a VPS, OpenDKIM is the usual signer. You do not need a full mail suite to do this.
Why the signature has to be yours
Without DKIM, a passing SPF only says the sending IP was allowed. A phisher on a hijacked marketing include, or a lookalike domain, still looks plausible. With DKIM plus DMARC alignment, the visible From domain has to match the domain that signed. That is the property inbox providers actually want.
A VPS that sends password resets should sign as the domain users see, not as a random hostname. Use a dedicated selector (the label before ._domainkey) so you can rotate keys without an outage. s1 and 202604 are both fine names. Avoid using default as the selector; everyone else's tutorial did that.
Install and generate a 2048-bit key
On Ubuntu 24.04 the packages are opendkim and opendkim-tools. Keep the private key readable only by the opendkim user.
apt-get update
apt-get install -y opendkim opendkim-tools
mkdir -p /etc/opendkim/keys/example.com
opendkim-genkey -b 2048 -s s1 -d example.com -D /etc/opendkim/keys/example.com
chown -R opendkim:opendkim /etc/opendkim/keys
chmod 640 /etc/opendkim/keys/example.com/s1.private
cat /etc/opendkim/keys/example.com/s1.txtThe s1.txt file is the DNS payload. Publish it as a TXT record on s1._domainkey.example.com. Split long strings into quoted 255-character chunks if your DNS panel requires it. Wait for the record to resolve from a public resolver before you send signed mail; a signature whose public key 404s is worse than no signature.
Wire OpenDKIM to Postfix as a milter
Minimal signing-only config for a box that sends as example.com:
# /etc/opendkim.conf
Syslog yes
Mode s
Canonicalization relaxed/simple
Domain example.com
Selector s1
KeyFile /etc/opendkim/keys/example.com/s1.private
Socket inet:8891@localhost
PidFile /run/opendkim/opendkim.pid
UserID opendkim
UMask 007
OversignHeaders FromIn Postfix main.cf, milter_default_action = accept so a down signer does not lose mail, and milter_protocol = 6:
smtpd_milters = inet:127.0.0.1:8891 non_smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters covers locally injected mail (sendmail, pickup), which is how most app servers submit. If you only set smtpd_milters, cron mail leaves unsigned. Restart opendkim then postfix.
SigningTable and KeyTable become worth it when you host more than one domain. For a single domain the Domain / Selector / KeyFile trio is enough and harder to misconfigure.
Body hash pitfalls
relaxed/simple is a conservative pair: header canonicalization tolerates whitespace changes, body canonicalization does not. That is what you want for transactional mail. Do not rewrite the body after OpenDKIM signs. A footer added by the MTA, or a transfer encoding change, invalidates bh=. If you must add a disclaimer, add it before the milter, not after.
Sign at least From, Subject, Date, Message-ID, To, and MIME-Version. OversignHeaders From stops a second From header being appended in transit. Keep the body under the l= limit if you set one; most small MTAs omit l= and sign the whole body.
Verify on a received copy, not on the sender
Send to a Gmail address you control. Open original and look for DKIM-Signature and Authentication-Results: dkim=pass header.d=example.com. If you see dkim=fail, the usual causes are a truncated TXT record, the wrong selector, or the body being altered. opendkim-testkey -d example.com -s s1 -vv prints whether DNS matches the local private key.
Rotate selectors instead of overwriting s1. Publish s2 with a new 2048-bit key, switch OpenDKIM to s2, wait a week so delayed mail still verifies against s1, then delete the old TXT. A 1024-bit key still verifies at some providers and is below what Gmail expects in 2026. Do not store the private key in git or in a world-readable backup of /etc. If you restore a VPS from an image, confirm the live selector still matches the key on disk before you send.
Takeaway
OpenDKIM on a small Postfix is enough to sign transactional mail. Publish a 2048-bit selector, milter both smtpd and local pickup, and confirm dkim=pass on a real inbox. You can spin up an Ubuntu 24.04 instance on Netbay in under 60 seconds and follow this signing path on a static Lucknow IP — netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance