It can’t be DNS. There’s no way it could be DNS. It was DNS.

This article is not a full tutorial, just thoughts and notes on my OpenBSD email server assembly process. This article from helped me greatly. I couldn’t be more thankful. Here is a wayback machine link in case the datacenter go boom.

Why OpenBSD?

And why not something else?

The Short: Because OpenBSD just works. Email isn’t hard. Linux (and even FreeBSD) are inclined overcomplicate it.

The Long: Before starting this project, I did a significant but intermittent amount of research into email servers and read many deprecated blog posts about the whole ordeal. There seemed to be a general superstition about how difficult email can be. The paranoia lurks, psyching out the hobbyist admin. No wonder with all the hearsay about rejection, DNS, reputation, and hacking.

It’s been nearly a year since I began thinking about hosting my own email. Obviously I adopted some of this superstition myself, or at least allowed it to enable my procrastination. Since this is a holiday weekend, I thought “What better to do than spend three days troubleshooting email?”

Initially I wanted to host the email server on FreeBSD but eventually landed on OpenBSD. Why? Because I wanted less headache. UNIX is like a loaded gun pointed at your foot. It is very easy to hurt yourself. OpenBSD mitigates some of this by mounting the loaded gun in a fixed position. It will require intense gymnastics to shoot yourself in the foot. OpenBSD seemed like the appropriate choice to minimize foot shooting, especially if I am providing email accounts for other people. I cannot in good faith provide a dumpster fire for my friends and colleagues, especially if there are important documents in that dumpster. The only flaming dumpster I endorse is the multi-user shell server created specifically for multiple users to start fires in.

Fires aside, I was pleasantly surprised with OpenBSD. I held a preconceived notion that ‘sometimes OpenBSD doesn’t do what you want it to do because it was designed to mitigate flaming dumpsters and foot-shooting.’ But boy was I wrong. From a desktop perspective, OpenBSD can feel limiting . . . but, in server space, OpenBSD feels very pleasant to use.

The Software Stack

Stop! Mailinabox and Mailcow are black boxes and we want nothing to do with them!

A mail server consists of multiple moving parts. Some would say ‘many’ moving parts, but I disagree. If you’ve ever had to set up a reverse proxy for your django or rails app, you’ve touched more moving parts than a basic email server.

Component Program/file What it does
MTA OpenSMTPD Sends and receives mail via SMTP
MDA Dovecot Provides IMAP/POP so that users can use the server via a client
MUA Thunderbird IMAP/POP client
Spam filter Rspamd Progressively learns what is and isn’t spam
Auth UNIX Auth Allows a user to connect using the same credentials they would use to log in to the system locally
Mailbox ~/Maildir Stores user email
LMTP UNIX Sockets Transfers mail between the MDA and the MUA

Typically, the mail process can look like this:

Bob's MUA (via IMAP/POP) -> Bob's MDA (via LMTP) -> Bob's MTA (via IMAP) -> Alice's MTA  
Alice's MTA (via LMTP) -> Alice's MDA -> Alice's Mailbox

When Alice wants to check her mail, it will look something like this:

Alice's Mailbox <- Alice's MDA <- Alice's MUA (via IMAP/POP)


They hate what they do not understand.

Modern email is a hack on top of DNS. In addition to an A record and an MX record, we need three TXT records: DMARC, DKIM, and SPF.

Type What it’s for
DMARC Instructs the sender what to do if unable to deliver
DKIM Server’s pubkey, used for cryptographically verifying the sender
SPF Tells other servers who is allowed to send mail from our server

And some example records:

Type Name Data TTL Priority
A mail 123.456.78.9 120
MX mail 123.456.78.9 120 10
TXT selector._domainkey.mail “v=DKIM1;k=rsa;p=a 128 bit RSA key ;” 120
TXT _dmarc.mail “v=DMARC1;p=quarantine;pct=100;rua=mailto:postmaster@mail.domain.tld;” 120
TXT mail ““v=spf1 ip4:123.456.78.9 -all” 120


Software stack

My biggest problem with the software stack was not actually the software stack. It was a DNS issue. For some god forsaken reason, Vultr automaticaly creates a * CNAME record. This is a problem because, depending on which record is found first, subdomain.domain.tld resolves to domain.tld. This was causing localhost to resolve to an external IP address.

Localhost binding to an external address is a big problem. Why? because if localhost resolves to anything other than a service might try to bind to 123.456.78.9:port instead of Redis and rspamd were both trying to bind to an external address and failing to start.

After I deleted the CNAME wildcard record, everything started just fine.


The biggest issue for DNS was that I actually misconfigured it. My mail server is actually at mail.domain.tld, not domain.tld. I Set up all my TXT records for domain.tld so google was automatically rejecting my email . . . because there was not valid DMARC, DKIM, or SPF. After modifying all the records so that they match the subdomian, I was still experiencing DKIM issues. In order to fix this, I copied the DKIM entry for mail.domain.tld to domain.tld and the issue seems to have been resolved.