# How do you set up DKIM on Postfix with OpenDKIM?

> Set up DKIM on Postfix with OpenDKIM: generate a key, wire the milter, publish the DNS record, and fix the common signing failures. A step-by-step guide.

To sign mail with DKIM on Postfix you run OpenDKIM as a separate service and connect it to Postfix as a milter. OpenDKIM holds your private key, adds a `DKIM-Signature` header to every outgoing message, and Postfix hands mail to it over a socket. The setup is four steps: install OpenDKIM, generate a key pair and configure the signing tables, publish the public key as a DNS TXT record, and point Postfix's milter directives at the OpenDKIM socket. Once the DNS record propagates, receivers can verify your signature and your mail earns a `dkim=pass`.

## Quick takeaways

- Postfix does not sign DKIM itself — **OpenDKIM** is a milter that plugs into it.
- You generate a **key pair**: the private key stays on the server, the public key goes in DNS.
- Three files drive OpenDKIM: the main config, a **KeyTable** (selector → key), and a **SigningTable** (domain → selector).
- Postfix connects to OpenDKIM through `smtpd_milters` / `non_smtpd_milters` pointing at a shared socket.
- The socket address in `opendkim.conf` must **exactly match** the one in Postfix's `main.cf`.
- After DNS propagates, confirm signing by reading the `Authentication-Results` header on a test message.

## What is OpenDKIM and why does Postfix need it?

Postfix is a mail transfer agent — it routes and delivers mail — but it has no built-in DKIM signer. [DKIM](/learning/what-is-dkim) signing is added by a *milter* (mail filter), a helper process Postfix streams each message through before delivery. OpenDKIM is the standard open-source milter for this job. When a message passes through, OpenDKIM hashes the headers and body, signs the hash with your private key, and inserts a `DKIM-Signature` header. The receiving server later fetches your public key from DNS and checks that signature.

Because OpenDKIM runs as its own daemon, the two processes talk over a socket — either a TCP port like `inet:localhost:8891` or a Unix socket like `local:/run/opendkim/opendkim.sock`. Keeping the signer separate is what lets one OpenDKIM instance sign for several domains and selectors at once. If the milter concept is new, our explainer on [what an MTA is](/learning/what-is-an-mta) sets the context for where signing sits in the mail flow.

## How do you install and configure OpenDKIM?

Install OpenDKIM and its tools from your distribution's packages (`opendkim` and `opendkim-tools` on Debian/Ubuntu, `opendkim` on RHEL-family systems). The tools package provides the key-generation utility.

**1. Generate a key pair.** Pick a selector — a short label that names this key, for example `mail` or `2026a`. Generate the key for your domain:

```
opendkim-genkey -b 2048 -d example.com -s mail -D /etc/dkimkeys/
```

This writes two files: `mail.private` (the private key, keep it readable only by the OpenDKIM user) and `mail.txt` (the public key, formatted as a DNS record). A 2048-bit key is the current baseline; 1024-bit keys are considered weak.

**2. Configure `/etc/opendkim.conf`.** Set the operating mode to sign and verify, and point OpenDKIM at its tables:

```
Mode                    sv
Socket                  inet:8891@localhost
Canonicalization        relaxed/relaxed
KeyTable                /etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
InternalHosts           /etc/opendkim/TrustedHosts
```

`Mode sv` signs outbound mail and verifies inbound. `relaxed/relaxed` canonicalization tolerates the minor whitespace changes mail servers make in transit, which avoids needless `dkim=fail (body hash did not verify)` results.

**3. Map the key with a KeyTable.** The KeyTable ties a selector name to a domain and a private-key file. One line per key:

```
mail._domainkey.example.com example.com:mail:/etc/dkimkeys/mail.private
```

**4. Choose what to sign with a SigningTable.** The SigningTable maps sender addresses or domains to a KeyTable entry. To sign everything from your domain:

```
*@example.com mail._domainkey.example.com
```

**5. List your trusted internal hosts.** `TrustedHosts` names the hosts whose mail OpenDKIM should sign (rather than only verify). Include loopback and your server:

```
127.0.0.1
localhost
::1
example.com
```

Set ownership so the OpenDKIM user can read the keys and tables, then restart the service.

## How do you publish the DKIM public key in DNS?

Open the generated `mail.txt`. It contains a TXT record for the host `mail._domainkey.example.com` with a value like `v=DKIM1; k=rsa; p=MIIBIjANBg...`. Publish it at your DNS provider:

- **Name / host:** `mail._domainkey` (your provider appends the domain automatically — do not type the full domain twice).
- **Type:** `TXT`.
- **Value:** the entire `p=` string. Some providers need the quoted segments concatenated into one continuous value; the key is long enough that DNS splits it into chunks, and it must reassemble to the exact string OpenDKIM generated.

DKIM/record-setup on the mail server side is only half the job — receivers cannot verify until this record resolves. Give it time to propagate, then confirm the selector is live with a [DKIM record lookup](/learning/check-dkim-record) or the [DKIM checker tool](/tools/dkim) against `mail._domainkey.example.com`.

## How do you connect OpenDKIM to Postfix?

With OpenDKIM running and listening on its socket, tell Postfix to route mail through it. Add these lines to `/etc/postfix/main.cf`:

```
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
```

- `smtpd_milters` filters mail arriving over SMTP (from your submission clients).
- `non_smtpd_milters` filters mail injected locally (from `sendmail`, cron, scripts) — set it too or that mail goes unsigned.
- `milter_default_action = accept` means that if OpenDKIM is unreachable, Postfix still delivers the message rather than deferring it. That trades a missing signature for uptime; use `tempfail` instead if you would rather retry than send unsigned.
- The socket in `smtpd_milters` **must match** the `Socket` line in `opendkim.conf` exactly.

Reload Postfix. Send a message to an external address you control and inspect the headers: you should see a `DKIM-Signature` header added by your server, and the receiver's `Authentication-Results` should read `dkim=pass`. Our guide to [testing and verifying DKIM](/learning/dkim-testing) walks through reading that header in detail.

## Common issues with DKIM on Postfix

### Mail leaves the server with no `DKIM-Signature` header

Postfix is not handing mail to the milter, or OpenDKIM is not signing it. Check that both `smtpd_milters` and `non_smtpd_milters` point at the socket, that the OpenDKIM service is actually running, and that the sender's domain is covered by a `SigningTable` line. Mail from a host not listed in `TrustedHosts` is only verified, never signed — a very common cause of silent non-signing.

### `dkim=fail (body hash did not verify)`

The body changed after signing, usually because a mailing list or a downstream filter altered it, or because canonicalization is too strict. Use `relaxed/relaxed` canonicalization so minor whitespace edits do not break the hash. Our deep dive on [why a body hash fails to verify](/learning/dkim-fail-body-hash-did-not-verify) covers the other triggers.

### Postfix logs "connect to Milter service … Connection refused"

Postfix cannot reach OpenDKIM on the socket. Confirm OpenDKIM is listening on the same address you put in `main.cf` — a mismatch between `inet:localhost:8891` and `inet:8891@localhost` versus a Unix socket path is the usual culprit. If you use a Unix socket, make sure it lives somewhere Postfix's chroot can reach and that permissions allow the connection.

### DKIM passes but DMARC still fails

A valid signature can still fail DMARC if it is not *aligned* — the signing domain (`d=` in the signature) must match the visible `From:` domain. If your SigningTable signs with the wrong domain, you get `dkim=pass` but `dmarc=fail`. Sign with the same domain your users send from, and see [DKIM vs SPF and how alignment works](/learning/dkim-vs-spf-difference) for the underlying rule.

## Frequently asked questions

### Do I need OpenDKIM, or can Postfix sign DKIM on its own?

Postfix has no native DKIM signer; you need a milter. OpenDKIM is the most common choice, but alternatives like `dkimpy-milter` exist. Whichever you pick, the wiring to Postfix — the milter socket directives — is the same.

### What selector name should I use?

Any short, DNS-safe label works; `mail`, `default`, or a dated value like `2026a` are common. The selector lets you run more than one key at once, which is what makes painless [key rotation](/learning/dkim-key-rotation) possible — you publish a new selector, switch signing to it, then retire the old one.

### Should the DKIM key be 1024-bit or 2048-bit?

Use 2048-bit. It is the current baseline that mailbox providers expect, and 1024-bit keys are treated as weak. Generate with `-b 2048`. If your DNS provider struggles with the longer record, split the value into quoted chunks that reassemble to the full key.

### Can one OpenDKIM instance sign for multiple domains?

Yes. Add a KeyTable entry and a SigningTable line per domain, each with its own selector and key file. This is the main reason OpenDKIM runs as a separate service rather than inside Postfix.

## Where Palisade fits

Running OpenDKIM yourself means owning key rotation, alignment, and the DNS record for every domain and selector on the box — and quietly discovering months later that one of them stopped signing. Palisade watches DKIM, SPF, and DMARC across all your domains, flags a selector that drops to `dkim=fail` or falls out of alignment, and keeps enforcement on track whether you self-host with Postfix or send through a platform. Check your current signing and alignment with the [Email Security Score](/tools/email-security-score).

## Related reading

- [What is DKIM? DomainKeys Identified Mail explained](/learning/what-is-dkim)
- [How to check a DKIM record and interpret the result](/learning/check-dkim-record)
- [How and when to rotate DKIM keys](/learning/dkim-key-rotation)
