Configure SPF, DKIM and DMARC to Stop Emails from Landing in Spam

Technical guide to configure SPF, DKIM and DMARC in your VPS DNS and stop landing in spam on Gmail and Outlook. Commands, examples and real verification.

Emails sent from a freshly provisioned VPS land in spam on Gmail and Outlook by default. The reason is rarely the message content — it is the absence of the three DNS authentication records that major providers expect to find: SPF, DKIM and DMARC. Without them, the destination server treats the email as “anonymous” and applies the most restrictive rule of the antispam policy.

This guide shows how to configure the three records from scratch on an Ubuntu 24.04 LTS VPS running Postfix, with a real DKIM key generated via OpenDKIM. The focus is to leave the configuration production-ready, validated by public tools, and with a progressive DMARC policy so it does not block legitimate emails during the transition. Estimated time: 30 to 45 minutes counting DNS propagation.

The persona here is the sysadmin who already has Postfix installed and sending emails — except those emails are going to the spam folder. If you are building the mail server from scratch, the same procedure applies after Postfix is receiving and sending.

Prerequisites

What you need before starting

Ubuntu 24.04 LTS VPS with root access via SSH. Postfix installed and configured to send emails. Your own domain with access to the DNS panel of the registrar (Registro.br, Cloudflare, etc). Reverse PTR already configured on the VPS IP pointing to the server hostname. Outbound port 25 enabled at the provider.

System Ubuntu 24.04 LTS
MTA Postfix 3.8+
DKIM filter OpenDKIM 2.11
SMTP port 25 (outbound)

Before touching DNS, confirm that Postfix is sending messages. Run echo "test" | mail -s "test" [email protected] and check the received header — you will see spf=none, dkim=none and dmarc=none in Authentication-Results. That is exactly what we are going to solve.

Configure the SPF record

SPF (Sender Policy Framework) is the simplest of the three records. It declares which servers are authorized to send emails on behalf of your domain. The destination server queries the SPF record of the envelope-from and compares it against the IP that originated the connection.

01

Identify the public IP of your VPS:

curl -s https://api.ipify.org

Write down the value — you will use it in the SPF record and at validation time.

02

In the DNS panel of your domain, create a TXT record at the root (@ or the domain itself) with the following content:

v=spf1 ip4:YOUR.IP.HERE.123 -all

The -all at the end is a hard fail: it instructs the recipient to reject emails from any other IP. If you use external services (Mailgun, Google Workspace), use include: before the -all:

v=spf1 ip4:YOUR.IP.HERE.123 include:_spf.google.com -all
One SPF per domain

If you already have a TXT record starting with v=spf1, edit the existing one — do not create another. Multiple SPF records cause PermError and authentication fails entirely, even with everything else correct.

03

Wait for propagation (usually 5-15 minutes) and validate:

dig +short TXT yourdomain.com | grep spf1

The output should return exactly the record you created.

Install and configure OpenDKIM

DKIM (DomainKeys Identified Mail) signs each email with a private key stored on the server. The public key sits in a DNS record — the recipient uses it to verify the signature. This protects against content modification in transit and proves that the email really came from your domain.

04

Install OpenDKIM and the utilities:

sudo apt update
sudo apt install -y opendkim opendkim-tools
05

Create the directory structure and generate the 2048-bit key:

sudo mkdir -p /etc/opendkim/keys/yourdomain.com
cd /etc/opendkim/keys/yourdomain.com
sudo opendkim-genkey -b 2048 -d yourdomain.com -s mail
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 600 /etc/opendkim/keys/yourdomain.com/mail.private

The command generates two files: mail.private (private key, stays on the server) and mail.txt (public key, goes to DNS). The selector mail is arbitrary — any string works, but use something descriptive if you plan to have multiple selectors in the future.

06

Edit /etc/opendkim.conf with the minimum working configuration:

Domain                  yourdomain.com
KeyFile                 /etc/opendkim/keys/yourdomain.com/mail.private
Selector                mail
Socket                  inet:8891@localhost
Mode                    sv
SubDomains              no
AutoRestart             yes
UMask                   002
Syslog                  yes
07

Integrate OpenDKIM with Postfix by editing /etc/postfix/main.cf:

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

Restart both services:

sudo systemctl restart opendkim
sudo systemctl restart postfix
sudo systemctl enable opendkim
08

Get the contents of the public key to place in DNS:

sudo cat /etc/opendkim/keys/yourdomain.com/mail.txt

The output has the format mail._domainkey IN TXT ( "v=DKIM1; ..." ). Copy only the content between the quotes — everything starting at v=DKIM1; and going to the end. Some keys come split into two strings inside parentheses — mentally concatenate them, removing the inner quotes and line breaks.

In the DNS panel, create a TXT record with:

  • Name: mail._domainkey
  • Value: the full key content (v=DKIM1; k=rsa; p=MIIBIjANBgkqh...)
DNS provider truncating the record

If the panel complains that the value is too large (over 255 characters), the key needs to be split into concatenated quoted strings. Most modern providers accept pasting the whole value and split it automatically — Cloudflare, Route53 and Registro.br do this. If yours does not, manually split into chunks of 200 characters between quotes, separated by spaces.

Publish the DMARC policy

DMARC (Domain-based Message Authentication, Reporting, and Conformance) ties SPF and DKIM to the domain name visible to the user (the From: header) and tells the recipient what to do when authentication fails. Without DMARC, SPF and DKIM are merely informational — the recipient picks the policy on its own.

09

In DNS, create a TXT record on the subdomain _dmarc:

The p=none policy is the mandatory starting point: you receive daily reports by email but no action is taken. This allows you to identify legitimate sources that are not yet configured before raising the policy.

10

After 2 to 4 weeks with p=none and the reports analyzed, move up to quarantine:

v=DMARC1; p=quarantine; pct=25; rua=mailto:[email protected]; fo=1

The pct=25 applies the policy to only 25% of emails that fail — useful to detect problems without full impact. After another 1-2 weeks without incidents, move up to pct=100 and then to p=reject.

Do not jump straight to p=reject

Going from p=none straight to p=reject before checking the DMARC reports frequently knocks out legitimate emails from third-party systems that sign on behalf of your domain (CRM, billing, monitoring). The reports show exactly which IPs need to be authorized.

Verify real delivery

DNS configuration does not guarantee that emails arrive in the inbox. Verification must be done with real messages delivered to real providers.

11

Send a test email to a Gmail address and open the received message. Click “Show original” in the Gmail menu and look for the Authentication-Results section:

Authentication-Results: mx.google.com;
  dkim=pass [email protected]
  spf=pass [email protected]
  dmarc=pass header.from=yourdomain.com

All three need to show pass. If any one shows fail, softfail or none, go back and review the corresponding record.

12

Use the mail-tester.com tool for a complete assessment: send an email to the address generated by the tool and access the report. The minimum acceptable score is 9/10 — below that there are pending adjustments.

Troubleshooting common issues

DKIM passes in tests but fails on Gmail

Almost always it is a discrepancy between the selector configured in OpenDKIM and the one published in DNS. Confirm with dig +short TXT mail._domainkey.yourdomain.com — the returned value must match the content of mail.txt exactly. Another frequent reason: the key was pasted with line breaks that the DNS panel did not strip.

SPF returns PermError

Indicates multiple TXT records starting with v=spf1 on the same domain. List them with dig +short TXT yourdomain.com and remove duplicates by consolidating everything into a single record.

Emails still land in spam even with everything passing

Check the IP reputation at mxtoolbox.com/blacklists.aspx. New IPs or IPs recently out of a blacklist need warm-up — start with low volume (5-10 emails/day to engaged contacts) and ramp up gradually over 4-6 weeks.

Next steps

  • Configure aggregate DMARC reports in a dedicated parser (postmark, dmarcian) to view unauthorized sources in a structured way
  • Add a secondary DKIM selector rotating every 6 months to maintain cryptographic hygiene
  • Implement MTA-STS and TLS Reporting to ensure delivery via strict TLS on providers that support it
  • Configure BIMI after p=quarantine or p=reject is stable for 30+ days — displays your logo in the inbox on Gmail and Yahoo

If you are putting transactional email into production, a Hostini VPS comes with editable reverse PTR through the panel and port 25 open by default — two common barriers that VPS from other providers leave closed.

Frequently asked questions

Can I have more than one SPF record for the same domain?

No. RFC 7208 forbids multiple TXT records starting with v=spf1 on the same domain — providers treat it as PermError and the entire SPF fails. If you need to authorize multiple sources (your own VPS + Mailgun + Google Workspace), merge everything into a single record with multiple include mechanisms.

What is the ideal DKIM key size, 1024 or 2048 bits?

2048 bits is the current standard and what Gmail and Yahoo have required since 2024 for senders sending more than 5,000 emails per day. 1024-bit keys still work technically, but are considered weak. The only practical limitation is that some DNS providers truncate TXT records over 255 characters — almost all today accept concatenated quoted strings, so there is no reason to stay at 1024.

How long does it take for DMARC to start impacting delivery?

The DNS record propagates in minutes to a few hours, but the real effect depends on the policy. With p=none you only receive reports — nothing changes in delivery. With p=quarantine or p=reject, providers start applying immediately after reading the record. It is recommended to run p=none for 2 to 4 weeks before moving up to quarantine, to analyze the reports and discover all legitimate sources sending through your domain.

Do I need reverse PTR configured on the VPS IP?

Yes. Gmail, Outlook and most corporate antispam systems reject or mark as spam emails coming from IPs without a valid PTR, or with a generic provider PTR (like 187-45-x-x.cloud.provider.com). The PTR should resolve to a hostname that points back to the same IP (forward-confirmed reverse DNS). On a Hostini VPS you configure the PTR through the panel.

Why do my emails land in spam even with SPF, DKIM and DMARC all passing?

Authentication is not reputation. If the VPS IP is new or has been used for spam before, providers start out suspicious regardless of DNS configuration. Check the IP at mxtoolbox.com/blacklists.aspx, do a gradual warm-up of the volume (a few emails per day rising slowly) and keep the bounce rate low. Email content also counts — suspicious attachments, shortened links and excessive images hurt.

Does DMARC with p=reject break forwarded emails?

It can break when the forwarding server modifies headers or the envelope without re-signing with DKIM. SPF almost always fails on forwards (the IP of the forwarding server is not authorized on the original domain). DMARC passes if DKIM passes, so keep the DKIM key strong. For mailing lists that rewrite the From, configure ARC or accept that part of the traffic will be affected.

Topics:
Next steps Ryzen cloud with NVMe storage and always-on DDoS protection.Go live on a Hostini VPS →
Was this tutorial helpful?
Chat on WhatsApp