How to configure SPF, DKIM and DMARC in Postfix to improve email delivery (step by step)

Redes & Segurança · By · Published on · Updated on · 8 min read

Brown envelope with partially visible cards and a wooden articulated hand beside it
Neutral visual for email and server configuration tutorials Photo: Lisett Kruusimäe / Pexels

This guide shows how to configure SPF, DKIM and DMARC in Postfix to improve email delivery. You’ll publish DNS records, sign messages with OpenDKIM, align DMARC and validate everything with tests. Suitable for Linux servers running Postfix in production or newly installed.

What we'll do and prerequisites

We’ll configure email authentication on three fronts. SPF declares who can send on behalf of your domain. DKIM signs messages. DMARC enforces policy and generates reports.

Prerequisites: root or sudo access, Postfix installed, control over your domain DNS and required ports open. If you use a firewall, open ports 25 and 587. See How to set up a firewall on Ubuntu with UFW.

If you’re building the server from scratch, this guide helps preparing the system: How to Install Ubuntu Server.

Important: set a FQDN hostname and configure PTR (reverse DNS) with your provider. Without a correct PTR, large providers often reject or severely penalize your mail.

  • Access to the domain DNS
  • Postfix installed and able to send/receive mail
  • Consistent FQDN hostname (e.g. mail.example.com)
  • Ports 25/587 open in the firewall
  • DNS propagation time (may take hours)
Record Type Name/Host Example value
SPF TXT @ (root of domain) v=spf1 mx -all
DKIM TXT default._domainkey v=DKIM1; k=rsa; p=PUBLIC_KEY
DMARC TXT _dmarc v=DMARC1; p=quarantine; rua=mailto:[email protected]

Verify domain, hostname and Postfix HELO

The From domain should align with Postfix HELO/EHLO and with your PTR. Validate and adjust myhostname and myorigin. Avoid a generic HELO or a literal IP.

Also check mydomain and the address used for locally generated mail.

# Ver seu hostname atual
hostname -f

# Conferir parâmetros relevantes do Postfix
postconf -n | egrep 'myhostname|mydomain|myorigin|smtpd_banner'

# Ajustar hostname (exemplo) — substitua pelo seu FQDN
sudo postconf -e 'myhostname = mail.exemplo.com'
sudo postconf -e 'myorigin = $mydomain'

# Reiniciar o Postfix após ajustes
sudo systemctl restart postfix

SPF: create and publish the TXT record

SPF declares which hosts can send mail for your domain. The record is a TXT at the domain root. Start with a moderate policy and harden after testing. Only one SPF record per domain—multiple records break validation.

If you also send through external services, include appropriate mechanisms such as include, ip4 or ip6.

  • -all blocks senders outside the list (strict)
  • ~all marks as suspicious but does not block (relaxed)
  • Use mx to authorize the domain’s MX hosts
  • Add ip4:/ip6: for specific IPs
  • Avoid exceeding the 10 DNS lookup limit
Example Description When to use
v=spf1 mx -all Only the MX hosts can send Simple domains, single server
v=spf1 ip4:203.0.113.10 a mx ~all Authorize fixed IP, A and MX Testing phase (more tolerant)
v=spf1 mx include:mailservice.example ~all Include a third-party service When sending via external providers
# Exemplo de linha de zona (provedores têm telas próprias para isso)
# Tipo: TXT | Nome/Host: @ | Valor:
"v=spf1 mx -all"

# Após publicar, valide a resolução
dig +short TXT exemplo.com | sed 's/\"/"/g'

DKIM: install and integrate OpenDKIM with Postfix

DKIM signs messages with a private key; recipients validate using the public key in DNS. We’ll use OpenDKIM as Postfix’s milter.

This is also a good time to ensure SMTP TLS. For free certificates, see How to get free SSL with Let's Encrypt.

# Instalar OpenDKIM e ferramentas
sudo apt update && sudo apt install -y opendkim opendkim-tools

# Diretórios e arquivos básicos
sudo mkdir -p /etc/opendkim/keys/exemplo.com

# Configuração principal do OpenDKIM
sudo tee /etc/opendkim.conf >/dev/null <<'CONF'
Syslog                  yes
UMask                   002
UserID                  opendkim:opendkim
Socket                  inet:8891@localhost
Selector                default
Mode                    sv
Canonicalization        relaxed/simple
AutoRestart             yes
OversignHeaders         From
KeyTable                /etc/opendkim/KeyTable
SigningTable            /etc/opendkim/SigningTable
TrustedHosts            /etc/opendkim/TrustedHosts
CONF

# Mapas de assinatura e hosts confiáveis
sudo tee /etc/opendkim/KeyTable >/dev/null <<'CONF'
default._domainkey.exemplo.com exemplo.com:default:/etc/opendkim/keys/exemplo.com/default.private
CONF

sudo tee /etc/opendkim/SigningTable >/dev/null <<'CONF'
*@exemplo.com default._domainkey.exemplo.com
CONF

sudo tee /etc/opendkim/TrustedHosts >/dev/null <<'CONF'
127.0.0.1
::1
localhost
*.exemplo.com
CONF

# Permissões
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod -R go-rwx /etc/opendkim/keys

# Habilitar o milter no Postfix
sudo postconf -e 'milter_default_action = accept'
sudo postconf -e 'milter_protocol = 6'
sudo postconf -e 'smtpd_milters = inet:localhost:8891'
sudo postconf -e 'non_smtpd_milters = inet:localhost:8891'

# Ativar serviços
sudo systemctl enable --now opendkim
sudo systemctl restart postfix

DKIM: generate the key and publish it in DNS

Generate a key pair per domain and selector. Publish the public key in DNS as a TXT at default._domainkey.yourdomain. Protect the private key with strict permissions.

After publishing, validate with the testing tool and only then start sending real mail.

  • Use 2048-bit key size
  • One selector per policy/key (e.g. default, s2026)
  • Rotate the key periodically
  • Copy only the p= value to DNS
# Gere a chave (substitua exemplo.com e o selector se desejar)
cd /etc/opendkim/keys/exemplo.com
sudo opendkim-genkey -s default -d exemplo.com -b 2048
sudo chown opendkim:opendkim default.private
sudo chmod 600 default.private

# Exiba a entrada DNS sugerida
cat default.txt

# Reinicie para garantir leitura das chaves
sudo systemctl restart opendkim postfix

DMARC: set policy, alignment and reports

DMARC tells recipients what to do when SPF or DKIM fail and where to send reports. Start with p=none to observe. Move to quarantine and reject once alignment is stable.

Alignment requires the From domain to match the domain validated by SPF or DKIM. Use adkim/aspf=s (strict) when you are confident.

  • p=none only monitors (no delivery impact)
  • p=quarantine sends suspicious mail to spam
  • p=reject rejects non-aligned messages
  • rua/ruf receive aggregate/forensic reports
  • pct controls the percentage of messages the policy applies to
Policy Effect Recommended use
p=none Collects reports only Initial phase to validate configuration
p=quarantine Suspect messages go to spam Intermediate, after reducing false positives
p=reject Rejects non-aligned messages Final phase when everything is correct
# Registro DMARC (TXT) — Nome/Host: _dmarc
# Ajuste emails de relatório para sua caixa dedicada
"v=DMARC1; p=none; rua=mailto:[email protected]; ruf=mailto:[email protected]; fo=1; adkim=s; aspf=s; pct=100"

# Após publicar, confira
dig +short TXT _dmarc.exemplo.com | sed 's/\"/"/g'

Tests: validate SPF, DKIM and DMARC

Validate DNS records first. Then check the DKIM key with the test tool and send a test email to an external inbox. Inspect Authentication-Results, DKIM-Signature and Received-SPF headers.

If you use SMTP TLS, confirm the certificate and negotiated cipher suites.

  • Confirm there is only one SPF record
  • DKIM check: correct selector and public key published
  • DMARC alignment: From matches validated domain
  • Send to various providers and compare headers
# SPF e DMARC
dig +short TXT exemplo.com | sed 's/\"/"/g'
dig +short TXT _dmarc.exemplo.com | sed 's/\"/"/g'

# DKIM: teste do selector (default) — substitua domínio
opendkim-testkey -d exemplo.com -s default -vvv

# Após enviar um e-mail de teste, veja logs locais
sudo journalctl -u postfix -n 100 | egrep 'DKIM|smtpd|policy' || tail -n 100 /var/log/mail.log

Common issues and how to fix them

Some errors repeat frequently. Check this list before deeper debugging.

Remember that DNS propagation can take hours. Immediate tests may fail due to caching.

  • Multiple SPF entries: keep a single record and combine mechanisms
  • Wrong DKIM selector: confirm default._domainkey.yourdomain
  • Private key permissions: 600 and owner opendkim
  • Milter not loaded: check smtpd_milters and port 8891
  • DMARC failures due to alignment: From differs from DKIM/SPF domain
  • Third-party services sending without SPF/DKIM: adjust include/keys

Frequently asked questions

Can I have more than one SPF record?

No. There should be only one SPF TXT record per domain. If you need to authorize multiple services, combine mechanisms in the same record (mx, a, ip4/ip6, include). Multiple SPF records cause validation to fail and harm deliverability.

Which DMARC policy should I use first?

Start with p=none to collect reports and adjust alignment. When stable, move to p=quarantine and then p=reject. Use pct to apply the policy gradually. Set adkim/aspf=s when everything is aligned.

What is the DKIM selector and how do I choose it?

The selector identifies which public key to use (e.g. default, s2026). It forms the DNS record name, like default._domainkey.yourdomain. Use one selector per active key. For rotation, publish a new selector/key and switch the configuration.

How long until the configuration starts working?

Local changes in Postfix and OpenDKIM take effect immediately after restarting services. DNS, however, can take minutes to hours to propagate depending on TTLs and caches. Always validate with DNS queries before wide testing.

Do I need TLS for SPF, DKIM and DMARC to work?

TLS is not required for these validations, but it is strongly recommended for security and reputation. Enable STARTTLS in Postfix with a valid certificate, preferably automated. TLS reduces interception risk and improves your server’s standing.

Need help tuning Postfix and DNS?

If authentication still fails or you want a configuration review before enforcing stricter policies, contact me for a focused support session.

Discuss technical support

See also