Building an email service with OpenSMTPD, OpenBSD's native mail server: arranging /etc/mail/smtpd.conf, choosing between maildir and mbox, configuring relay and virtual users, securing with STARTTLS, and considering DKIM and SPF for deliverability.

In episode 16 you built a web frontend with relayd. Now we run a service older than the web itself: email. And on OpenBSD, email means OpenSMTPD — a lightweight, secure, and very easy-to-configure native mail server.
Email is a complex and often misunderstood protocol: sending (SMTP), receiving (SMTP + POP/IMAP), authentication, and anti-spam. OpenSMTPD handles the SMTP side elegantly, while applying the OpenBSD principles you already know: secure by default, minimal configuration, and pledge/unveil.
rcctl enable smtpd
rcctl start smtpd
rcctl status smtpdThe configuration is at /etc/mail/smtpd.conf. After changing it, validate and reload:
smtpd -n
rcctl reload smtpdThe smtpd.conf structure consists of listen (receiving mail) and action/match (determining the treatment):
listen on egress
action "local" mbox alias <aliases>
match from any for domain example.com action "local"listen on egress receives mail on all interfaces.action "local" mbox stores mail as mbox in the local user's mailbox.match ... for domain example.com directs mail for that domain to the local action.OpenSMTPD supports two storage formats:
| Format | Description | Best for |
|---|---|---|
mbox | One file per user (/var/mail/user) | Simple, the default |
maildir | cur/, new/, tmp/ directories per user | More reliable for many-client access |
For maildir, the action is written:
action "local" maildir "/home/%{user}/Maildir" alias <aliases>The maildir advantage: no file locking, more resistant to crashes. Many administrators choose maildir for servers with many users.
To send email (not just receive), define a relay action:
action "outbound" relay
match from any mail-from any for any action "outbound"The match ... relay line forwards mail to the internet. For authentication to an external relay (for example an SMTP provider), use:
action "relay" relay host smtp://mail.example.com:587 auth <secrets>The authentication details are stored in a separate file (/etc/mail/secrets) read by OpenSMTPD — never put passwords in smtpd.conf.
To serve many domains/users without system accounts, use virtual users. The database can be a text file or a database:
table aliases db:/etc/mail/aliases.db
table virtuals db:/etc/mail/virtuals.db
action "vmail" maildir "/var/vmail/%{dest.domain}/%{dest.user}" virtual <virtuals>
match from any for any action "vmail"virtuals.db contains the mapping of addresses to virtual users. All virtual mail is stored in /var/vmail, separate from system accounts — the standard way to host mail.
Warning
Database files for tables (for example aliases.db and virtuals.db) must be generated from the source text file with makemap. If you edit the text file directly without makemap, the changes won't be seen by smtpd.
Encrypted mail requires STARTTLS. Using the certificates from episode 10:
listen on egress tls pki example.comThis configuration encrypts SMTP traffic. Certificates are managed with acme-client as usual, with rcctl reload smtpd after renewal.
Email that doesn't use DKIM/SPF will land in spam. Two mechanisms you need to understand:
dkimproxy as a package to sign outbound mail.Install and enable dkimproxy:
pkg_add dkimproxy
rcctl enable dkimproxy_out
rcctl start dkimproxy_outThen point smtpd through the proxy:
action "outbound" relay host smtp://127.0.0.1:10027DKIM works as an SMTP filter: outbound mail passes through dkimproxy, which signs it, then it's forwarded. SPF is just a matter of adding a TXT record to your DNS zone.
Mail is a system that must be constantly watched:
rcctl status smtpd
tail -f /var/log/maillog
smtpctl show queue
smtpctl show sessions/var/log/maillog records every mail transaction. smtpctl show queue shows the waiting queue — if it's long, there's a delivery problem. smtpctl show sessions shows active connections.
Info
Before going live in production, test your mail from an external service and check maillog for relay or TLS errors. Emails that fail to send pile up in the queue — and a bloated queue is the first sign of a problem.
In episode 17 you built an email service with OpenSMTPD: enabling smtpd, arranging /etc/mail/smtpd.conf, choosing between maildir and mbox, configuring relay and virtual users, securing with STARTTLS, and handling DKIM, SPF, and monitoring.
Key takeaways:
listen and action/match are the heart of smtpd.conf.makemap.In the next episode, episode 18, we'll run virtualization with vmm and vmd — OpenBSD's built-in type 1 hypervisor, managing VMs with vmctl, building tap-based networking, and taking snapshots for testing environments.