Postfix - OpenDKIM configuration

1 - How to configure DKIM ?

This page is a step by step page that will show you how to sign outgoing email with the DKIM specification.

OpenDKIM will sign the email passed by Postfix and every receiver can check the email authenticity through the public key published in the DNS record of the domain.

The signature is added in the email in an header field. Example:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;;; t=1591988898;

The configuration is done with OpenDkim installed as a milter software of Postfix (for the MTA) on a CentsOS (Redhat) linux box.

3 - Steps

To install opendkim as a milter to an MTA, you need to perform the following steps:

3.1 - Installation

If you want to install it from the source. See the install doc
  • install the EPEL repository
  • install OpenDKIM

yum install -y opendkim

3.2 - Generate the key and the DNS record with opendkim-genkey

The opendkim-genkey will:

The private key signs the message (ie encrypt) and the public key can decrypt it.

As there is only one public key that can decrypt a message encrypted by the private key, the authenticity of the sender can then be verified.

This is a feature of the Public Key Cryptography system.


opendkim-genkey -s SELECTOR -d DOMAIN
# example with the hostname
opendkim-genkey -s $(hostname) -d


  • SELECTOR is simply a name given to the key generally the hostname (not the fully-qualified domain name). The selector is used in the name of the DNS record.
  • DOMAIN is the DNS name of the domain
If you want to test it, you can add the --testmode argument.

testmode tells the verifiers that they should not take any real action based on success or failure of the use of this key after verifing a message.

In the generated TXT record, you will find back a t=y that you can remove to enter in production mode.

Two files should have been generated:

  • the private key with the following name $(hostname).private
  • the txt record (and the public key as value) with the following name $(hostname).txt

Verification of the file existence:

  • the TXT record with the public key where you can verify your domain at the end of the file.

cat $(hostname).txt	IN	TXT	( "v=DKIM1; k=rsa; "
	  "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQUtHxTD63yxwq5fmjJ3RtXw2NP5/QEiSq3Xx85faTHnnj3/PA/igwWaueDsoeUuZOpkL74gDNGWBoQPecRaFrAXdPoEKGDYNBeMXzIkWQOth9Oaq4N+38LV08Ui86so8B2BhcvgXiqpACsrPur0hbDQWI183tZve7MKMPs3KPIQIDAQAB" )  ; ----- DKIM key for
  • The private (Should stay private, not shared). The private key encrypt the message and the public key decrypt.

ll $(hostname).private

-rw------- 1 root root 887 Jun 12 12:03

3.3 - DNS Zone

The next step is to publish the public key by creating a TXT DNS record with the help of the generated TXT file.

3.3.1 - DNS DKIM value explained

The TXT record value generated has the form:

"v=DKIM1; k=rsa; " "p=public key"

where :

  • v=DKIM1 is the DKIM protocol version.
  • p=… is the public key (base64 encoded)
  • k= provide a list of mechanisms that can be used to decode a DKIM signature such as RSA

3.3.2 - DNS DKIM Creation

If the DNS interface of your provider supports TXT input, you can just take the input of the TXT file and add it at the end.

Then you should see a TXT record with a DKIM value. Example with a snapshot of the DNS manager of my domain provider

where the public key DNS record should appear as a TXT resource record as:

# in the snapshot


DOMAIN may also be specified:
  • the Domain option in the configuration file,
  • or the domain that would be returned from a KeyTable lookup.

3.3.3 - DNS DKIM record test

You can verify that the key matches the private key with opendkim-testkey

opendkim-testkey -d DOMAIN -s $(hostname) -k $(hostname).private
# example
opendkim-testkey -d -s $(hostname) -k $(hostname).private

If you got no message and an exit code of zero, that's all good.

echo $?


3.4 - Private key

The key should be stored in a safe place.

  • As we installed it with the package, we will store it /etc/opendkim/keys <note info>In the official doc, you will see /var/db/dkim/</note>

mv $(hostname).private /etc/opendkim/keys

  • The storage directory and the associated private key file should be owned by the user that will be executing the filter (preferably not the superuser) and be mode 0700 and 0600 respectively.

# The opendkim was created with the package installation
chown opendkim:opendkim /etc/opendkim/keys/ # should already by the case
chown opendkim:opendkim  /etc/opendkim/keys/$(hostname).private
chmod 0700 /etc/opendkim/keys # 0=rwx
chmod 0600 /etc/opendkim/keys/$(hostname).private # o=rw

3.5 - OpenDKIM Configuration

The minimal configuration for OpenDKIM defines:

FYI: The global configuration file can be found at:


3.5.1 - Socket selection

The MTA and your opendkim filter communicate over a socket connection.

You can choose to use:

In case of a tcp socket, for selinux, the following command will need to be executed as the superuser, which declares the chosen port to be a milter application port:

# to see if SELINUX is enable
# cat /etc/selinux/config | grep -i SELINUX=
# if yes then
semanage port -a -t milter_port_t -p tcp <port>

The actual socket configuration of OpenDKIM is

##  Create a socket through which your MTA can communicate.
Socket	inet:[email protected]

3.5.2 - Mapping Key to Domain - KeyTable

OpenDKIM find the private keys with the KeyTable lookup file. It's a file that maps key names to signing keys. (private key)


  • Enable the key table <note tip>refile means regular expression file (ie regular expression are permitted)</note>

KeyTable refile:/etc/opendkim/KeyTable

  • then change the mapping in the /etc/opendkim/KeyTable by adding the following line

SELECTOR._domainkey.DOMAIN DOMAIN:SELECTOR:/path/to/my/key
# or with this actual step by step
SELECTOR._domainkey.DOMAIN DOMAIN:SELECTOR:/etc/opendkim/keys/SELECTOR.private

It requires SigningTable be enabled. - SigningTable

This SigningTable is a file that maps:

  • email addresses (ie from the From: header field)
  • to one or several SELECTORs
  • in order to apply one or more signatures to outgoing messages.
  • and is a prerequisite of KeyTable


  • Global conf <note tip>The refile scheme enable regular expression in the SigningTable file . You could also used file to disable it</note>

SigningTable refile:/etc/opendkim/SigningTable

  • SigningTable

# Wild card example (refile scheme)

3.5.3 - Operating mode

The operating modes are:

  • s (sign)
  • v (verify) - default
  • sv (sign and verify)

You need to change it at minimal to s in order to sign the email.

Mode	sv

3.5.4 - Signing external mail


If you wish to sign mail that comes from sources other than the localhost address (, include these in CIDR notation in the configuration file for the InternalHosts configuration option.

##  Identifies a set "internal" hosts whose mail should be signed rather than verified.
InternalHosts refile:/etc/opendkim/TrustedHosts

  • See also:

##  Identifies a set of "external" hosts that may send mail through the server as one
##  of the signing domains without credentials as such.
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts

3.6 - MTA configuration (Postfix )

3.6.1 - smtpd_milters

  • smtpd_milters You specify SMTP-only Milter applications (there can be more than one) with the smtpd_milters parameter. Each Milter application is identified by the name of its listening socket;


smtpd_milters = inet:localhost:portnumber ...other filters...

With the OpenDkim Socket configuration of this tutorial (the default)

smtpd_milters = inet:localhost:8891

3.6.2 - non_smtpd_milters

The non-SMTP Milter applications handle mail that arrives via:

  • the Postfix sendmail command-line
  • or via the Postfix qmqpd server.

non_smtpd_milters = inet:localhost:8891

3.6.3 - Error handling


The milter_default_action property defines what to do in case of errors.

The following value can be chosen:

  • tempfail (default) respond with a temporary error status, so that the client will try again later.
  • accept to receive mail as if the filter does not exist
  • reject to reject mail with a permanent status.
  • quarantine action is like “accept” but freezes the message in the “hold” queue (from Postfix 2.6 or later)

milter_default_action = tempfail

3.7 - Restart

Restart the services

systemctl restart opendkim
systemctl restart  postfix

3.8 - Test

To test, we need to send an email and to see if the DKIM header was added.

If the test does not work, check the diagnostic section.

We show two ways to test:

  • via mail-tester - graphical and all-in test solution
  • via gmail - just send a message to check the header

3.8.1 - Install a Mail Client

The client mail used is mail that you can install with the mailx package

yum install -y mailx

It's possible to send email with the sendmail command line of postfix

3.8.2 - Mail tester

The first easy method to test is to send an email to the mail-tester website. They will then analyze it and give you a score.

echo "Body: This is a test mail. Hallo Charlie" | mail -s "Subject: A big test" [email protected]

3.8.3 - Gmail

Gmail allows you to see:

  • the original email (header)
  • and the test that they performs.

Send an email to your Gmail address

echo "Body: This is a test mail. Hallo Charlie" | mail -s "Subject: A big test" [email protected]

You can then verify the signing:

  • or quickly in the dropdown:

4 - Diagnostic

opendkim-testkey -d DOMAIN -s $(hostname) -k /etc/opendkim/keys/$(hostname).private

  • Check the mail log - postfix and openDKIM use syslog, you should find the log then in /var/log/ such as:

tail -f /var/log/maillog

  • Example of log where you can see that opendkim added the DKIM-Signature field.

Jun 12 20:57:17 vps748761 postfix/pickup[22362]: D572A1FBE8: uid=0 from=<root>
Jun 12 20:57:17 vps748761 postfix/cleanup[23285]: D572A1FBE8: message-id=<[email protected]>
Jun 12 20:57:17 vps748761 opendkim[23276]: D572A1FBE8: DKIM-Signature field added (,
Jun 12 20:57:17 vps748761 postfix/qmgr[22361]: D572A1FBE8: from=<[email protected]>, size=521, nrcpt=1 (queue active)
Jun 12 20:57:18 vps748761 postfix/smtp[23288]: D572A1FBE8: to=<[email protected]>,[]:25, delay=0.57, delays=0.06/0/0.35/0.16, dsn=2.0.0, status=sent (250 2.0.0 OK  1591988238 r8si6848556wrn.39 - gsmtp)
Jun 12 20:57:18 vps748761 postfix/qmgr[22361]: D572A1FBE8: removed

  • Check that the DKIM process is started on the Socket port

netstat -tulpn|grep 8891

tcp        0      0*               LISTEN      21636/opendkim

  • Verify the postfix configuration

postconf -h smtpd_milters # inet:localhost:8891
postconf -h non_smtpd_milters # inet:localhost:8891

5 - Documentation / Reference

This page was created with the help of the following references:

Data Science
Data Analysis
Data Science
Linear Algebra Mathematics

Powered by ComboStrap