So You Want To Run Your Own Mail Server…

Whenever the subject of running your own mail server comes up, there’ll always be two people who chime in.

The first will say “No, don’t do it! It’s a virtually impossible thing to do these days!”

The second will say “Don’t listen to that guy! It’s trivial! I just installed one and I had no problems!”

Both are right, and both are wrong: It’s very much possible to run your own MTA (Mail Transport Agent), but there’s a lot of steps required if this is to be successful (where “successful” is here defined as “Gmail won’t put your email in the spam folder”). Every one of these steps are trivial, but you have to suffer through many TLA and ETLA concepts that there’s really no point in knowing anything about.

I mean, just to run a mail server.

So I’ve written a script that does it all for you. Other than the DNS changes that you’ll have to do, but it’ll tell you exactly what you have to put where.

The script requires a Debian or Ubuntu host, and it’ll install a whole lot of stuff. If you already have a partial mail server setup, it may break the server. Nothing’s guaranteed.

After running it, you’ll have a host with (ETLAs incoming) the exim4 SMTP server (with TLS, DKIM signing, ClamAV and Spamassassin), Dovecot IMAP (with TLS), SPF, DMARC and certificates from Let’s Encrypt.

If you’re a spammer (I mean “marketing expert”), please go away. This is not about being able to send out newsletters; it’s for people who want to run their own mail servers for whatever reason (for instance, to avoid the monoculture of Gmail).

tl;dr:

Download the script

curl -O https://raw.githubusercontent.com/larsmagne/make-mta/master/make-mta.sh

and then run it. Follow the instructions, and you’re done.

Longer version:

Running a mail server doesn’t take much work: After it’s up it, it’s probably going to stay up. However, I’m going to go through all through all the steps the script does, so that if you’re interested you can understand how it all fits together.

This is going to be overly detailed: I’m going to go through everything, point by point, from provisioning a server to configuring your mail client. Prepare to scroll.

The assumptions are: You own a domain, and you want to send out mail from that domain, and you want to receive mail for that domain. In my case, I have the domain “eyesore.no”, and I’ll be using that throughout all the examples.

First of all, you need a name for the server. “mta” is nice, so I’ll call mine “mta.eyesore.no”. Now that the difficult part is over (“there’s only two difficult things in IT: Naming, caching and off-by-one errors”), you need a server.

Any hosting provider is fine: Let’s go with DigitalOcean. After you’ve gotten an account there, click “New Project”, give it a name, and then “Create”.

Create a Ubuntu LTS image; the cheapest one they have. An MTA requires virtually no resources, but if you expect to keep a lot of incoming mail on your IMAP server, you may want one with more storage than 25GB.

These days, it’s nice to have IPv6 support, but it doesn’t really matter much.

You need a way to log in. I strongly recommend adding your public ssh key here, but you can also use a password.

And here’s where you enter that name you came up with earlier.

Then create the server (or “droplet” as DigitalOcean cutesely calls it).

Within a few seconds, your server is created, and that number is the IPv4 address of the server (and the unhelpfully shortened IPv6 address).

Now go to your DNS provider (and you’ll be doing a lot of things here, so keep this window open) and add some resolving.

I’m using Cloudflare, because it’s… nice?

That’s an “A” record for the IPv4 address…

… and an AAAA record for IPv6.

Now you can ssh to the server:

Download the script

curl -O https://raw.githubusercontent.com/larsmagne/make-mta/master/make-mta.sh

and then run it:

This will do basic stuff like “apt upgrade”. Nice to have the system somewhat up-to-date.

This will install and enable the ufw firewall, and open the ports we need to run an MTA (ssh, smtp, imaps, http). fail2ban is also installed.

The script will then try to figure out the host name based on the IP address. If it can’t, then it means that reverse DNS wasn’t added automatically, and you have to add a PTR DNS record for the IP address (both IPv4 and IPv6, if you have that). All MTAs need to have reverse IP records, otherwise many MTAs will refuse to accept mail from the server.

This will start a standalone http server and use the Let’s Encrypt servers to acquire a TLS certificate. Answer the questions it asks.

Next, it’ll fetch and configure exim itself (along with SpamAssassin and clamav), and set up DKIM. (DKIM is a way to sign mail so that others can verify that it comes from your mail server.)

This server accepts mail for you, so you need a way to read it. IMAP is the preferred way for most. This IMAP server will also use the Let’s Encrypt certificates to authenticate the connection.

We’re done! That is, we only have to make these DNS changes that the script has summarised. So let’s do them, one by one, in the Cloudflare interface.

First, the DKIM public key. This will allow other MTAs to verify that an email came from this MTA.

Then the SPF data. This says that your MTA is allowed to send mail from your domain.

Then the DMARC data. This says that you should do with failures from DKIM and SPF. (Note that this DMARC policy is very relaxed; you may want to make it more strict.)

Finally, add an MX record for the domain. This means that all incoming mail for the domain will go to this mail server.

Now the server is all set up… except that you probably need a user that’ll receive email there.

You may want to choose a different name (if you have a different name).

Let’s see what the mails look like now:

Mail-Tester is a convenient tool to test how it all went.

Perfect!

Even Gmail can’t complain about your mail now.

Well… unless they do. They’ll probably find some other hoops to run through any day now, but for the moment you should be OK. Oh, and if you’ve provisioned a server that happened to get the IP address of a previous notorious spammer, you may find that your mail gets tagged as spam, anyway. In practice, if you use a reputable hosting service, this isn’t a big problem, but it can happen. If that’s the case, try again and get a new IP address.

Anyway, it might be helpful to also show how to connect to the MTA, right? As an example, here’s Evolution:

Basic info.

IMAP on port 993 (with TLS).

Outgoing email on port 465, with TLS and authentication. Done!

The reason I started thinking about writing this script (and blog post) is twofold: I think it’s a shame that we’re devolving from a decentralised infrastructure for email to a centralised one (i.e., Gmail). We can all see where Gmail is heading — towards a total silo, but it hasn’t quite gotten there yet, because they still have to interoperate somewhat with the rest of the world.

The other is that it’s really annoying that (apparently) nobody has done this before. It’s not like any of the things that the script does is difficult: It’s just that if you don’t know what you’re supposed to do, you can spend hours Googling around, and you’ll mostly get outdated information. For instance, if you search for “exim authentication”, you’ll find this official-looking page that gives horrendous tips like “You also need to add the Debian-exim user into the shadow group, so as to give Exim access to /etc/shadow via PAM.”

No! You should absolutely not let the exim process read the /etc/shadow file, because that’s a way to escalate bugs in the exim server.

And so on.

The script is on Microsoft Github, and pull requests are very welcome. I’m sure there’s many things that can be improved.

9 thoughts on “So You Want To Run Your Own Mail Server…”

  1. “These days, it’s nice to have IPv6 support, but it doesn’t really matter much.”

    No, it’s not “nice to have”. It’s necessary. With IPv4 addresses going for $30+/ea on the open market, the only sustainable way to grow is standing up new services with IPv6.

    It’s also a bit like saying “64-bit CPUs are nice to have, but it doesn’t matter much”. Sure, a 32-bit CPU can do everything you theoretically need to do, but if you’re deploying greenfield, you buy a 64-bit CPU.

    1. It’s nice to have, but it doesn’t really matter much. The vast majority of MTAs out there is IPv4-only, and there will be no repercussions if your MTA is, too. (For instance, DigitalOcean blocks all outgoing SMTP connections via IPv6.)

  2. Any reason you chose that incredibly awkward date format as DKIM selector? I mean, year,day,month?! Not even in Leftpondia is that a common date format. So is there some point to obfuscating the fact that it’s a date?

    1. Man, you think my new, disruptive date format isn’t going to take off? OK then, I’ve changed it to something more traditional.

  3. Oh what perfect timing, I was long planning to migrate my super-old mail server (still on Debian stretch…) to a new system and was dreading all that horrible setup stuff. So thank you! This will be very helpful. Originally I had planned to also migrate to Postfix because everyone and their dogs told me that it is SO MUCH better and more secure and whatnot, but now that you did all the googling and I guess I just stay on good old Exim, security be damned…

  4. Hello. It seems that the script has slightly changed. Before the Let’s Encrypt question, it asks the following question:

    Use MTA-STS to declare that all traffic should use TLS? (y/n)

    I chose Yes but then it says to add a CNAME DNS record.

    Make the following CNAME DNS record for mta-sts.

    I’m using the same format as you for my hostname mta.. I’m using Namecheap and just not sure how to configure the CNAME record

  5. Hi Lars,

    I stumbled on your make-mta.sh mostly by accident.

    About 15 years ago, I did something similar to that for byname.net to produce autonomous individual owned mail servers in large scale. ByName/By* provides the mail server software. You register your domain with ByName and run your own mail service. Consider my own email address as “emacs@mohsen.1.banan.byname.net” as an example.

    To do that, exim and bind won’t cut it. So I have been using qmail and djbdns. These are not well supported anymore but their design is clean and elegant. I only mention this here because you specialize in email. Compared to exim, the qmail ecosystem is far more complex and potent.
    All the echo-s that you have for DNS can be automated with djbdns.

    Qmail can also be of interest to you in the context of Gnus. You can create a submit-client service under Gnus and instead of the lisp SMTP of emacs use that submit-client. I have done this on my own but if you were to further integrate it in Gnus you get local queueing and better email tracking.
    You can also integrate things like priority, receipt-notification, delivery-report-notification between Gnus and qmail-submit-client with an 822-bus (Mail Submission Pipeline).
    See http://www.by-star.net/PLPC/180051 (Multi-Account Resident Mail Exchanger Environment for some details.
    Don’t try running anything based on that. It is dirty and incomlete but paging through it will give you some ideas.

    ByName MTA is part of http://www.by-star.net. That is the bigger picture. You may get a kick out of that …
    Again, thanks for all of your good work.

Leave a Reply