Sign outgoing mail with DKIM
DKIM adds a cryptographic signature to every outgoing message. Receiving servers check the signature against a public key published in your DNS, which proves the message really came from your domain and was not changed on the way. This article installs OpenDKIM, generates a key pair, connects it to Postfix, and publishes the public key.
Before you begin
- Postfix working for
example.comonmail.example.com, with TLS configured (see the Secure Postfix with TLS article). - Access to your DNS settings, such as Lantern DNS, LightServe's DNS editor.
- The SPF record already published (see the SPF article), so that both checks support each other.
- A sudo user on the server.
Step 1: Install OpenDKIM
OpenDKIM is the open-source program that signs outgoing mail. The tools package includes the key generator and a test command.
sudo apt update
sudo apt install -y opendkim opendkim-tools
Step 2: Generate a key pair
A DKIM key pair has a private key, which stays on your server, and a public key, which goes into DNS. The selector is a short name, here mail, that lets you rotate keys later without downtime. The key is stored in a folder named after your domain.
sudo mkdir -p /etc/opendkim/keys/example.com
sudo opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s mail -v
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 600 /etc/opendkim/keys/example.com/mail.private
The command creates two files: mail.private (keep this secret) and mail.txt (the public record). The chmod 600 line makes the private key readable only by its owner.
Step 3: Map domains to keys
OpenDKIM uses three small tables. The key table says which key belongs to which selector and domain. The signing table says which sender addresses to sign. The trusted hosts list says which machines may send through the signer without being checked.
sudo nano /etc/opendkim/KeyTable
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
sudo nano /etc/opendkim/SigningTable
*@example.com mail._domainkey.example.com
sudo nano /etc/opendkim/TrustedHosts
127.0.0.1
localhost
mail.example.com
example.com
Step 4: Configure OpenDKIM to use the tables
Add these settings to the end of the main configuration file. They tell OpenDKIM to sign in relaxed mode, read the three tables, run as its own user, and listen on a local network port that Postfix can reach.
sudo nano /etc/opendkim.conf
Mode sv
Canonicalization relaxed/simple
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
InternalHosts refile:/etc/opendkim/TrustedHosts
Socket inet:8891@localhost
UserID opendkim
Check that no older Socket line overrides this one. The command below lists only the active settings:
grep -v '^#' /etc/opendkim.conf | grep -v '^$'
If you see a different Socket line, such as one pointing at a file path, delete it so only the port version remains.
Step 5: Connect Postfix to OpenDKIM
Postfix sends each message through a filter, called a milter, that OpenDKIM provides. Give Postfix the address of the OpenDKIM socket, and tell it to keep delivering mail if the filter is unavailable so that a crash never stops your mail.
sudo postconf -e 'milter_protocol = 6'
sudo postconf -e 'milter_default_action = accept'
sudo postconf -e 'smtpd_milters = inet:localhost:8891'
sudo postconf -e 'non_smtpd_milters = inet:localhost:8891'
sudo systemctl restart opendkim postfix
sudo systemctl status opendkim --no-pager
The status should show Active: active (running). If it does not, the logs explain why: sudo journalctl -u opendkim -n 30 --no-pager.
Step 6: Publish the public key in DNS
Print the public record. The file is formatted for a zone file, so you need to take only the value part.
sudo cat /etc/opendkim/keys/example.com/mail.txt
The output looks something like this, with the key shortened here:
mail._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...." )
Create a TXT record named mail._domainkey on your domain. Its value is the two quoted strings joined together, with no quotation marks in the middle and no line breaks:
v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA....
Make sure you copy the whole key. A truncated key fails verification silently.
Step 7: Test the key
After the DNS change has spread, ask OpenDKIM to look up the record and compare it with the private key:
sudo opendkim-testkey -d example.com -s mail -vvv
A working setup ends with a line that says key OK. If you see key not found, wait and try again, or check the record name with dig +short TXT mail._domainkey.example.com.
mail.private. Anyone with that file can sign mail as your domain. If it leaks, generate a new key with a new selector and publish it, then remove the old record.Troubleshooting
- "key not found" in the test: the TXT record name must be exactly
mail._domainkey, followed by your domain. Check for typos and for a panel that appends the domain automatically. - Postfix cannot reach OpenDKIM: confirm it listens with
sudo ss -tlnp | grep 8891. The port must match inopendkim.confand in the Postfix settings. - OpenDKIM fails to start with a permission error: run
sudo chown -R opendkim:opendkim /etc/opendkimand restart the service. - Messages show
dkim=failat the receiver: the published key differs from the one on the server, usually from a copy-paste error. Re-publish the value frommail.txt.
Next steps
Your mail is now signed. Next, the DMARC article publishes a policy that asks receivers to check both SPF and DKIM, and tells them where to send reports. Start in monitoring mode so you can see the results before you enforce anything.
Something not working? Chat or email our support team and we will help you get unstuck.
Contact support