NewHalo Edge 3 is here: HTTP/3 everywhere and 42 edge locations.Learn more
Docs / Email servers

Sign outgoing mail with DKIM

Email servers9 min readUbuntu 24.04 LTS

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.com on mail.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.

Careful: never share or commit 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 in opendkim.conf and in the Postfix settings.
  • OpenDKIM fails to start with a permission error: run sudo chown -R opendkim:opendkim /etc/opendkim and restart the service.
  • Messages show dkim=fail at the receiver: the published key differs from the one on the server, usually from a copy-paste error. Re-publish the value from mail.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