How to Setup Mailgun - And What You're Actually Doing

Why You Need an Email Service

Managing an email server is a terrible experience. Emails fail to send. They get dropped. You get a ton of spam. You need to worry about backups and server security. Quite frankly, there's just too much to worry about when you can easily setup an email provider (for your inbox) and an email service (sending transactional/marketing emails).

Why spend the hours setting up and maintaining an email server when you can spend under $10/mo for both the provider and service? I don't know about you, but my time is worth more than that. Especially if it removes the possibility of me needing to dig through server logs to fix something.

Furthermore, once you run through this setup once, you'll realize just how simple it really is. Create an account, change a few DNS records, reuse your code, and boom. You're in business.

Why Mailgun?

I have a number of posts in the works and 3 of them utilize Mailgun. So instead of having a variation of the same steps in multiple places, I decided that I'd create one guide that I could reference elsewhere.

For now, this guide will only handle sending emails. You can setup Mailgun for your inbox as well, but I haven't needed that service yet. Instead, I'll either set up a noreply@example.com or setup an alias where replies go to my matt@example.com inbox.

Because I'm a developer and their tagline is "The Email Service for Developers." I've used other services but, in all honesty, Mailgun is the one that made the most sense to me. Other email services seem to gloss over why you do certain things (I'm looking at you, DNS records) whereas Mailgun is very straight forward as to why you're doing what.

It also offers both SMTP and API email sending, which is a definite perk. If I have the choice, I'll always use the API. But, in rare occasions (say, when I'm taking over an existing project), I'll be stuck using SMTP.

I won't cover SMTP in this post (or probably ever) because it's slower, offers more points of potential failure, usually requires more work to setup, and requires your email password. API sending is typically faster, has fewer points of failure, is easier to setup (in my opinion), and requires an API key that you can revoke and generate at will.

Creating Your Account

Creating a Mailgun account is pretty straight forward. They have an incredibly generous free tier that lets you send 10k emails per month, free. However, if you want to send to more than 5 different email addresses, you need to enter a credit card. They do this to prevent spam and, in my opinion, is pretty reasonable. You still don't get charged until you surpass 10k emails/month. I typically enter a card and have my clients do the same.

Choose Your Subdomain

Why am I using a subdomain when I want to send the emails from matt@example.com? It's to avoid conflicts and keep your domain in good standing. It's similar to having your frontend at example.com and your api at api.example.com. It lets you use each subdomain for something specific.

In this case, you're also using a different subdomain in order to keep your emails in good standing. The last thing you want to do is to send a marketing campaign from @example.com, get flagged for spam, resulting in all your regular emails are also getting flagged.

So, we do what Mailgun suggests. We use a subdomain. Mailgun also recommends splitting your transactional and marketing emails into separate subdomains, but I don't because I don't spam. You can use mail.example.com but I don't like to because I like to route mail.example.com to my email provider. Instead, I use mg.example.com.

Note: Even if you setup mg.example.com, you can still send emails from example.com.

What's that DKIM Authority you ask? It's added security and proof that you are who you say you are. The SPF record allows a host to send emails on your behalf whereas a DKIM hash proves that you sent the email. A good analogy is that SPF proves that snail mail came from your house whereas DKIM proves that the snail mail came from you (and not your sister). It does this using public cryptography.

Covering public key encryption is a complex topic to cover in depth (and quite frankly, not something I'd be equipped to teach). I'll explain it in another post, but the values 1024 and 2048 will be the number of bits in your private key. Choosing 2048 will require extra steps when setting your TXT records below and you may need to reach out to your DNS provider. 1024 is simpler, what I'd recommend, and if you're using this guide, it's most likely secure enough. If you want to read more on the subject, check out this writeup on StackExchange.

Configuring Your DNS Records

The first few times I fiddled with DNS records, I didn't really know what I was doing. After a while, I started to realize that I was only changing A records when I was directing traffic to certain IP addresses. Then I was changing MX records to setup my gmail accounts. Then TXT records. And CNAME. Eventually, I started reading up on what these records actually do. I suggest you do the same. Until then, this is what you need to know about the DNS records set for Mailgun.

Note: Depending on your DNS provider, you may or may not need to append the ".example.com" to the end of the hostname. As far as I'm aware, most providers won't make you, so "mg.example.com" would be entered as "mg".

All email services will tell you to setup a few different types of records. Some will be required while others are optional.

TXT Records (required)

Note: These are fake keys, use the ones that Mailgun generates for you

TXT records are the free-form DNS record. You can put pretty much any text here. There are, however, some standards. Mailgun will ask you to setup 2 of these. If you look at the beginning of the values, you'll notice that they actually mean something.

  • mg.example.com has a value that starts with v=spf1. That looks familiar for a reason. This is the TXT record that tells the world, "I have permission to send emails from this host."
  • krs._domainkey.mg.example.com is used to verify DKIM. The krs._domainkey may vary, but the value structure will be the same and will start with k=rsa. This may (or may not) mean much to you, but rsa is a specific algorithm used for encryption. Basically, the server that's sending the email sign the email with a one way hash generated with the private key (that only you should have, or in this case, Mailgun). The receiver will use the public key (the value after p=) to decode the signature in order to verify it came from you.

2048 DKIM Warning - You may need to reach out to your DNS provider to determine how to set this TXT record. Not all providers handle 255+ length TXT records the same way, and some don't handle them at all. TXT records have a max value length of 255 characters. If you use 1024 bit encryption, the value will be short enough that you can simply copy and paste the value into your DNS provider. If, however, you use 2048, the value will be too long. What you need to do instead, is break the value up into strings. So, break the string into 2 parts, wrap them each with double quotes, and put a space between the two strings. Put the two strings in a single TXT value.

# Provided String
k=rsa; p=MIIBIjANB....IDAQAB

# Edited String (there are no new lines, only spaces)
"k=rsa; p=MIIBIjANB...iu2HUUlWa" "3lH5ekwG...IDAQAB"

It doesn't matter where you split the value, so long as neither chunk is longer than 255 characters. Verify with your DNS provider that this is the right method as it can vary based on provider.

MX Records (optional)

Note: These keys will be the same for you (except with your domain)

MX Records are the records used for your mailbox. I never set these up because I use gmail/outlook/protonmail for my email service. Having competing values in your MX records can result in you got getting your inbound mail. We don't want that so we'll leave that alone.

CNAME Record (optional)

Note: These keys will be the same for you (except with your domain)

CNAME Records are used to route traffic. We're basically saying that email.mg.example.com should be handled by mailgun.org. When anyone visits email.mg.example.com, mailgun.org will receive the request and handle it accordingly. In this case, it's where the email's pixel will be routed. Which effectively allows Mailgun to track when emails are opened.

Wait...

Now we wait for Mailgun to verify that you've added the TXT and CNAME records. Sometimes it only takes 5 minutes. Sometimes it takes hours. It can take up to 48 hours though. However, you can start writing your code, so you don't have to twiddle your thumbs while waiting :).

Sending a Test Email

Testing is super simple. Mailgun even gives you sample code to use. I used my setup for this domain below, make sure to swap in your values.

// Code provided by Mailgun (and edited for readability/promise usage)
const mailgun = require('mailgun-js')
const mg = mailgun({
    	apiKey: API_KEY, 
    	domain: 'mg.example.com'
	})

// The following are both valid email address structures:
//   'you@example.com'
//   'You <you@example.com>'
const data = {
    from: 'Fake Matt <fake@mattharris.io>',
    to: 'alias@mattharris.io',
    subject: 'Mailgun Example',
    text: 'Wow, this setup was easy!'
    // You can use html instead of text
    // html: `<html>...</html>`
}

try {
    await new Promise((resolve, reject) => {
        mg.messages().send(data, function (err, body) {
            if (err) return reject(err)
            return resolve(body)
        })
    })
}
catch (err) {
    // Error handling goes here
}
Notice that I can send from @example.com even though I setup mg.example.com

Run the code and you should receive an email in your inbox! By inspecting the email details, we see the following values. Now you know what the recipient will see :).

A few things to pay attention to:

  • The recipient will have no idea that you used Mailgun to send the email. The only references to Mailgun is the subject (that I set) and the "mailed-by" and "signed-by". Developers (that use Mailgun) will probably be able to piece togethter how you sent the email, but that's it. And if you send from m.example.com, then they'll have no idea. Honestly, developers will know that you personally didn't send the emails, so who cares if they can tell from this information. They can check out your TXT records if they really wanted to know.
  • I can send from @example.com even if I registerred mg.example.com.
  • I can send from an email that doesn't exist. "fake@mattharris.io" isn't a real address. That's why there's no profile image. (I created alias@mattharris.io for this example so I could actually receive the email).

Hopefully by now you understand more of what's happening behind the scenes/why you need to do the things Mailgun tells you to do.\

As always, feel free to post any comments/questions below and I'll get back to you. Or feel free to ask me on Twitter @mattharrio.