> ## Content Index
> Fetch the complete content index at: https://blog.sudowl.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# New Mail Domain From Zero: SPF, DKIM and DMARC Passing on Day One
- URL: https://blog.sudowl.com/new-mail-domain-spf-dkim-dmarc-from-zero/
- Published: 2026-10-24T10:00:00.000Z
- Updated: 2026-10-25T17:35:47.000Z
- Description: Setting up a brand-new mail domain on a self-hosted panel: DNS records in the right order, DKIM selector, Gmail "show original", alignment, PTR and HELO.
- Author: Lukas
- Tags: Linux, Security

My new brand sudowl.com needed a real mailbox. Not a forwarder, not a Workspace tenant, just an `info@` on my own server, the one that runs KeyHelp. A brand-new domain has no reputation at all, so if anything is off on day one, mail goes straight to spam and you never hear about it.

Good news: it's not hard. You need five DNS records, one test mail to Gmail and a bit of patience with the order.

**TL;DR:** Mail host first (A record, certificate, SNI), then MX, SPF, DKIM, DMARC with `p=none` and `rua`. Send a test to Gmail and read "Show original": you want `spf=pass`, `dkim=pass` with your domain, `dmarc=pass`. Check that the server's PTR and HELO name match. Then switch your web apps to the new sender and test those too, because they're usually the first thing that breaks alignment. Tighten DMARC once every sender passes.

## The records

All examples with `example.com` and `203.0.113.10` as the server IP. My real setup looks the same, just with my domain.

```text
mail.example.com.             A     203.0.113.10
example.com.                  MX    10 mail.example.com.
example.com.                  TXT   "v=spf1 a mx ip4:203.0.113.10 -all"
default._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIIBIjANBg..."
_dmarc.example.com.           TXT   "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

```

The order matters a bit:

1. **A record for the mail host.** You need it before anything else, because the certificate for `mail.example.com` can't be issued without it. In my case a wildcard A record already covered it.
2. **Certificate and SNI on the server.** Clients connect to `mail.example.com`, so Postfix and Dovecot need to present that name.
3. **MX.** Only once the host is reachable with a valid cert. Otherwise the first mails from outside hit a server that isn't ready.
4. **SPF, DKIM, DMARC.** Before you send the first real mail.

My DNS for this domain is at the registrar, not in KeyHelp. KeyHelp's own DNS is switched off for that domain, so I typed the records in by hand. If your panel runs the zone, it does most of this for you.

## KeyHelp side

In the panel: the domain gets "email domain" switched on, and KeyHelp creates a DKIM key for it with the selector `default`. That's why the DKIM record above is `default._domainkey`. On my install the key files sit under `/var/lib/rspamd/keyhelp/dkim/keys/example.com/`, Rspamd does the signing.

For the mail host name I created `mail.example.com` as its own domain in the panel, with email and Let's Encrypt on. Only then does KeyHelp write the SNI maps for Postfix and Dovecot. On my box Postfix choked on the generated `hash:` map, the fix is in [Postfix SNI: map lookup problem](/postfix-sni-hash-map-lookup-problem/). After every panel update I check that it's still `texthash:`.

If all you have is a private key and you need the public part for the TXT record:

```bash
openssl rsa -in dkim.private.key -pubout -outform DER 2>/dev/null | openssl base64 -A

```

That's the `p=` value. More about the panel itself in [KeyHelp as a free Plesk alternative](/keyhelp-free-plesk-alternative/).

## Check the DNS before you test mail

```bash
dig +short MX example.com
dig +short TXT example.com
dig +short TXT default._domainkey.example.com
dig +short TXT _dmarc.example.com

```

Ask a public resolver too (`@8.8.8.8`), not only your local one. If the DKIM record comes back split into several quoted strings, that's normal, TXT strings are max 255 characters and get joined. What's not normal is a space or a line break in the middle of the key because the registrar's web form mangled it.

## Gotcha: SPF "a" means the domain's A record

`a` in SPF is the A record of the domain itself, not of the mail host. It works in my case because the bare domain points to the same server. If your website is somewhere else, `a` lets the web host send mail as you and does nothing for your mail server. `mx` covers the mail server, `ip4:` makes it explicit. Pick what's true for your setup and end with `-all` once you're sure, since a new domain has no old senders to break.

## The test: Gmail "Show original"

Send a mail from the new mailbox to a Gmail address. Open it, three dots, "Show original". The top of that page shows the verdicts, and the full header has a line like this:

```text
Authentication-Results: mx.google.com;
       dkim=pass header.i=@example.com header.s=default;
       spf=pass smtp.mailfrom=info@example.com;
       dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=example.com

```

What to look at:

- `dkim=pass` with **your** domain in `header.i` / `header.d`. A pass for some other domain doesn't help DMARC.
- `spf=pass` for `smtp.mailfrom`, the envelope sender.
- `dmarc=pass`, which means at least one of the two passed **and** is aligned with the `From:` header.

Mine came back with all three pass and landed in the inbox. Then test the other direction: mail from outside to the new mailbox, and check `/var/log/mail.log` that it got delivered locally.

## Alignment, short version

DMARC doesn't care that SPF passed for *some* domain. It cares that the domain that passed matches the `From:` header. Relaxed alignment (the default) accepts subdomains, so `bounce.example.com` counts for `example.com`. Strict (`adkim=s; aspf=s`) wants the exact domain.

This is where web apps get you. The mail server signs and sends correctly, then some app sends with `From: info@example.com` through a different server, or with an envelope sender on another domain, and DMARC fails.

## Gotcha: the contact form is a sender too

My website's contact form and my small toolbox app both send mail. Before the switch they sent from an address on an older domain. Changing the recipient to the new mailbox was the easy part. Then I changed the sender too, so the mails come `From:` the new domain.

The moment you do that, those mails have to pass SPF and DKIM for the new domain. So the app has to go through the server that holds the DKIM key, not straight out to the internet from a container. I sent a test from inside the container afterwards and checked the headers again: pass, pass, pass, inbox.

Rspamd's report on that test had two things worth knowing:

- `SEM_URIBL_FRESH15`: the domain in the mail is freshly registered. That's a score hit every new domain gets, and it goes away as the domain gets older. Nothing to fix.
- The app didn't set a `Date:` header, which costs a point. Small thing, but a new domain doesn't have points to spare. I fixed the mailer.

Read the spam score headers on your test mails, not only "did it arrive".

## PTR and HELO

Receivers also look at the server, not just the domain:

```bash
dig +short -x 203.0.113.10        # PTR of the server IP
dig +short A server.example.net   # must point back to 203.0.113.10
postconf myhostname               # what Postfix says in HELO

```

The PTR, the A record of that name, and the HELO name should all be the same hostname. That's forward-confirmed reverse DNS, and missing it is a classic reason for spam folder or outright rejects. The PTR is set at your hosting provider, not in your DNS zone.

It does **not** have to be `mail.example.com`. My server has one hostname with matching A and PTR, and it sends for several domains. One IP usually has one PTR, so with many domains on one box that's the only way anyway. SPF and DKIM are what tie the mail to your domain.

## Then: rua and moving up

`p=none` with `rua` first. The reports tell you who sends as your domain and whether it passes. How to read them is in [DMARC reports explained](/dmarc-reports-explained-rua-xml/).

For an old domain with years of unknown senders, I'd watch for a few weeks before tightening. A brand-new domain is the easy case: there's exactly one server and the apps you just switched yourself. Once all of those passed in the tests, I went to `p=quarantine` with strict alignment on the same day:

```text
_dmarc.example.com.  TXT  "v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto:dmarc@example.com"

```

The cost of strict: every new sender you add later needs envelope sender and DKIM on exactly this domain. Write that down somewhere, because in half a year you'll add a newsletter tool and forget.

## Gotcha: the mail service needs RAM

Turning on mail on a web box adds Postfix, Dovecot, Rspamd and maybe ClamAV. On a small server that's not free. I measured it in [mail server RAM: 4 GB vs 8 GB](/mail-server-ram-budget-4gb-vs-8gb/).

## Checklist

1. A record for `mail.`, certificate, SNI working for SMTP and IMAP.
2. MX, SPF (`-all`), DKIM (selector checked), DMARC `p=none` \+ `rua`.
3. `dig` against a public resolver.
4. Test to Gmail: spf, dkim (your domain), dmarc all pass, check the spam headers.
5. Test inbound.
6. PTR, A and HELO match.
7. Every app that sends as the new domain: switched and tested.
8. Reports for a while, then quarantine or reject.

Clients get `mail.example.com`, IMAP 993 with SSL and SMTP 587 with STARTTLS. That's it.

The reports from step 8 go into a small DMARC monitor I built, which sends me a weekly summary instead of XML. It's on [sudowl.com/en/dmarc-monitor](https://sudowl.com/en/dmarc-monitor?ref=blog.sudowl.com), free while it's in testing, and my own domain is the first one running on it. Any other report reader does the job too, as long as somebody actually looks.

---

### Related posts

- [DMARC Reports Explained: What the rua XML Actually Tells You](https://blog.sudowl.com/dmarc-reports-explained-rua-xml/)
- [My Backups Were Silently Dead for 7 Weeks: How I Check Backup Freshness Now](https://blog.sudowl.com/backups-silently-dead-check-backup-freshness/)
- [Mail + Web on One Server: 4 GB or 8 GB of RAM? I Measured It](https://blog.sudowl.com/mail-server-ram-budget-4gb-vs-8gb/)