From practice

DKIM: the pitfalls

The key is right, the service is running, the checking tool reports "key OK" — and nothing gets signed anyway. A debugging session to read along with, including every dead end actually walked into.

Starting position

A domain sends sign-up links and confirmations. SPF is fine, DMARC is set to "quarantine". What is missing is the signature — and without it no message survives a forward.

On the server the signing software was already installed, and the mail server had it in its configuration. It looked like twenty minutes' work.

Pitfall 1: the service counts as dead and is running anyway

$ systemctl status opendkim
× opendkim.service - OpenDKIM Milter
     Active: failed (Result: exit-code)
    Main PID: 1144719 (code=exited, status=69)

$ ss -lntp | grep 12301
LISTEN 0 128 127.0.0.1:12301 users:(("opendkim",pid=16866))

Both were true at once. The process numbered 16866 had been running since the previous year, started outside the service manager and with a different configuration file from the one being maintained. The mail server had been talking to that leftover process the whole time.

The start-up error status=69 simply meant "address in use" — a message that sounds like a network problem and in truth pointed at an undead process.

Lesson: when a service will not start, first check whether it is perhaps already running. And if so: with which configuration file.

Pitfall 2: running, verifying, but not signing

After the clean-up the service ran properly, and the key demonstrably matched the published DNS record. Messages still went out unsigned — and the log said nothing about it.

The cause was a missing line. Without an explicit statement of the operating mode, the service only verified incoming signatures instead of producing outgoing ones. And because there is nothing to verify about an outgoing message, it did nothing at all — and wrote nothing about that either, because the default setting logs successes, not the absence of work.

Lesson: a log with nothing in it is no proof that nothing happened. It can also mean that nobody was asked.

Pitfall 3: one asterisk, two meanings

With reason-logging switched on, the next cause was visible in a single line:

opendkim[1160093]: 666ACC09CA: no signing table match for 'no-reply@mailv2.com'
opendkim[1160093]: 666ACC09CA: no signature data

The lookup table contained *@mailv2.com — the notation found in practically every guide on the internet. It applies, however, to tables that are read as a regular expression; there the software translates the asterisk itself.

The table was included as a plain table, and there the comparison is literal: first the complete sender address, then the domain. *@mailv2.com is neither, and therefore matches nothing. The service ran flawlessly and signed nothing.

The bitter part: a pattern that matches everything in one kind of table and nothing in the other produces no error. It produces silence.

Lesson: when copying a configuration line out of a guide, copy the line above it as well — that is the one deciding how the line below is read.

Pitfall 4: the name of the key cannot be freely chosen

The public key has to appear in the DNS under a name you choose yourself. The provider's management interface, however, allowed no free-form records, only templates for well-known mail delivery providers — and the template assigns the name itself.

The choice was therefore between "no DKIM at all" and "a name that sounds like a service provider the domain has nothing to do with". The name won. Functionally flawless, semantically nonsense — and an example of how a technically open system founders on interfaces that know only the usual cases.

What helped in the end

A single setting, present in most signing programs and switched off by default: logging the reason why a message was not signed.

It made both remaining causes visible within seconds, after hours had gone by on guesswork. Anyone setting up DKIM should switch it on from the first moment and switch it off again only once operation has been stable for a while.

The check that proves something

That the key matches the DNS record says little — it only proves that two numbers belong together. The only meaningful test is the complete path: send a real message through your own application, keep the result at the destination, and check the signature with an independent tool against the published key.

That is the difference between "the header is there" and "the signature is valid". In the case described, two faults lay between the two.

The pattern

Four pitfalls, and three of them never made themselves known. No error, no warning, no conspicuous line in the log — the messages kept going out, just unsigned.

The same pattern is described on the page about what running your own mail server really costs. DKIM is merely its sharpest example: a procedure whose failure is entirely invisible, because it blocks nothing when it is missing.

Sources

Evidence for the claims on this page. Every text here is written from scratch; the sources are there to be checked against, not to be copied from.

Last reviewed: 2026-08-16