Run a Mail Server (No, Not Like That)

Everyone says don't run your own mail server. They're right. Fighting spam filters, maintaining IP reputation, dealing with blocklists, configuring DKIM and SPF and DMARC correctly, handling bounces, managing queues - it's a full-time job. The big providers have teams dedicated to deliverability. You don't want to compete with that.

I was a server admin right out of college, and mail was one of those areas where I built real understanding through real pain. That was the early 2010s, and it was brutal then. It's not worth even attempting now.

But there's a difference between running a mail server and running a mail relay.

A mail server receives mail, stores mailboxes, fights spam, and sends directly to the internet. A mail relay just speaks SMTP locally and hands off to an external service for actual delivery. You get the standardization benefits of SMTP without the deliverability nightmare.

SMTP is still how applications expect to send email. Every sign-up flow, every password reset, every notification - they all want SMTP credentials. And when you're self-hosting a growing collection of apps, you have two bad options:

  1. Per-app credentials: Each app gets its own API key or SMTP login from your email provider. Now you're managing credentials scattered across a dozen apps, and if you switch providers, you're updating a dozen configurations.

  2. Shared credentials: All apps use the same credentials. Simpler, until one app has a bug that sends a thousand emails and gets your account flagged. No isolation.

A local mail relay gives you a third option: apps talk SMTP to a local service, and that service handles provider credentials, rate limiting, and routing. Switch providers once, centrally. Isolate apps from each other. Keep the universal standard that every app already supports.

That's what I built with dokku-mail.


The Homelab Problem

My homelab started as a place to tinker. Single user, single admin, no friction. But as I added more useful things, I wanted to share them with family. And that's when email became a blocker.

Sign-up flows don't work without email. Password resets don't work without email. Without logins, the app is useless. I set up Immich for family photos, created one account manually, and shared the credentials. It worked, but it felt wrong.

I wanted proper user management: individual accounts, self-service password resets, maybe SSO. So I looked at LLDAP and Authelia. Both need email. Then I looked at the next three apps on my list. All need email.

Each time, I was copying SMTP credentials around, configuring the same environment variables, debugging the same connection issues. The solution was obvious: treat mail as a shared service, not as a per-app problem.


What dokku-mail Does

dokku-mail is a Dokku plugin that provides a local SMTP relay backed by external providers. You create a mail service, configure a provider, link it to your apps, and they automatically receive SMTP credentials pointing at the local relay.

# Install the plugin
dokku plugin:install https://github.com/deanmarano/dokku-mail.git mail

# Create a mail service
dokku mail:create production

# Configure your provider (Resend, AWS SES, Mailgun, SendGrid, or generic SMTP)
dokku mail:resend:setup production

# Link to your apps
dokku mail:link production immich
dokku mail:link production lldap
dokku mail:link production authelia

Each linked app receives SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, and MAIL_URL environment variables. They talk SMTP to the local relay. The relay handles authentication with your actual email provider.

Switch providers? Update one configuration. Add a new app? One link command. No credential sprawl.


The Provider Reality

Choosing a provider was supposed to be the easy part. I started with AWS SES because I was already using it for this website's authentication emails. The setup worked. I could send mail to my two verified email addresses. I configured three apps before inviting a family member and discovering the sandbox limitation.

AWS SES starts you in a sandbox where you can only send to verified email addresses. To get out, you submit a request explaining your use case. My request was denied without explanation. I appealed. Denied again.

I'm sure there's a path through the AWS bureaucracy, but I didn't have the patience. I switched to Resend, had production sending working in about ten minutes, and moved on with my life.

Switching from SES to Resend was a single configuration change. My apps didn't know or care - they kept talking SMTP to the same local endpoint.

dokku-mail supports Resend, AWS SES, Mailgun, SendGrid, and generic SMTP.


Building on dokku-dns

Email deliverability still depends on DNS records even when you're using external providers. SPF tells receiving servers which IPs can send mail for your domain. DKIM provides cryptographic signatures. DMARC ties them together with a policy. Get these wrong and your mail lands in spam.

dokku-dns, my previous plugin, only handled A records for app domains. So I extended it to support TXT and CNAME records, then built dokku-mail on top of that foundation. When both plugins are installed:

dokku mail:dns:setup production example.com

This creates the SPF, DKIM, and DMARC records through whatever DNS provider you've configured (Route53, Cloudflare, or DigitalOcean). The records are provider-specific - Resend has different DKIM requirements than AWS SES - and the plugin handles those differences automatically.

No more copying TXT records from provider dashboards. No more debugging SPF syntax errors.


Living With It

I've been running dokku-mail for about two weeks now. Six services across three domains share a single Resend-backed relay. Last week I added a third domain and another app with no friction - one link command and it worked. Adding email to a new app is one command:

dokku mail:link production newapp

The app restarts with SMTP credentials injected. Sign-up flows work. Password resets work. I don't think about email configuration anymore.

For local development, there's a mock provider that accepts all mail without sending anything. Apps get valid SMTP credentials they can connect to, useful for testing sign-up flows without spamming real inboxes.


Next Up: dokku-sso

DNS was the first piece of shared infrastructure my growing homelab needed. Email was the second. Authentication is the third.

Right now I'm testing dokku-sso, which will provide SSO and user management as a Dokku service. The core scripts are working. The idea is the same: create an auth service, link it to your apps, and they automatically get OIDC configuration injected.

Combined with dokku-dns for automatic DNS and dokku-mail for automatic email, the goal is a complete foundation for self-hosted apps that you can actually share with other people.

More on that soon.


Check it out: github.com/deanmarano/dokku-mail

Comments

Leave a Comment

Used for confirmation only. Not displayed publicly.