38f072b2d5

28 Jan 2016


Easy private email hosting with dockermail

So you own a domain and would like to host your own email but the set-up is a massive pain?

No problem! There is a tool just for that: dockermail.

It's a modular email service in docker containers, which is very easy to configure.
In this post I will run you through setting up your very own email server from scratch.

Install

For this tutorial you will only need to have docker and docker-compose installed.

Head over to docker.com and follow the 'getting started' instructions on your platform. In my case I will be using Ubuntu Linux.

docker-compose is not essential but will make launching and linking containers a breeze.

Configuration

To be on the right side of SPAM filters we will be using DomainKeys Identified Mail (DKIM) signing, this will sign all outgoing mail with a secret key, which recipients can verify against a public one you will publish through your DNS records.
And to complete the circle, we will also filter incoming email for SPAM.

First let's create a couple of folders.
We will need a place for our email, you can let the container keep it in a volume, but for safe keeping and backup you should mount it to a host folder. In our case we will create a folder /opt/dockermail/mail.

And finally for our settings create /opt/dockermail/settings and open a terminal in there.

DKIM key

There is a script in the opendkim module of dockermail that will generate a DKIM key with a minimum of fuss.
It will fetch a copy of the opendkim image if you don't have it already, generate a key and dispose of the container when its done.

You can either download this script and run it locally, or you can run it straight from the web:

bash <(curl -s https://raw.githubusercontent.com/adaline/dockermail/master/opendkim/generate_key.sh) [your-domain.tld]

Just replace [your-domain.tld] with the domain you will be sending mail from.
For example let's use awesome-company.co.uk:

$ bash <(curl -s https://raw.githubusercontent.com/adaline/dockermail/master/opendkim/generate_key.sh) awesome-company.co.uk
Generating keys for awesome-company.co.uk
Add a TXT domain record with key: "default._domainkey" and value:
---------
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwTDvwqhb9K0hEbyOBKc9/HE2q8Uv8ndMUW7PeQIepTUD23gWlXL+s4w0taS5BdLAq8DRe5BA7FoE0FbMk3fC7iq96rJrsiYeKED2tDjcbyTfZfhw/eLdEURFkb0fyMUuv73XACGvuVgdXXJMHdM4keu5Hn/uEAHa5UkzZZpUUwIDAQAB
---------
Printing a private key: awesome-company.co.uk.opendkim_key

You should now have a file awesome-company.co.uk.opendkim_key in your settings folder.

Go to your domain name provider and add a TXT record with the key default._domainkey and the long string you got from the command above as the value, in our case:

v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwTDvwqhb9K0hEbyOBKc9/HE2q8Uv8ndMUW7PeQIepTUD23gWlXL+s4w0taS5BdLAq8DRe5BA7FoE0FbMk3fC7iq96rJrsiYeKED2tDjcbyTfZfhw/eLdEURFkb0fyMUuv73XACGvuVgdXXJMHdM4keu5Hn/uEAHa5UkzZZpUUwIDAQAB

Accounts

Both the email core module and the incoming SPAM filter use the same single settings file - config.json, which looks like this:

{
  "settings": {
    "myhostname": ""
  },
  "domains": {
  }
}

myhostname under settings should be the hostname of your mail server. In our example we will just use awesome-comapny.co.uk but you may want to use mail.awesome-company.co.uk if you have that DNS record set up, or perhaps even epic-mail-server.com if you want a white-label email for multiple domains!
Remember to set the MX record for your domain to the same hostname.

Let's update our config.json file:

{
  "settings": {
    "myhostname": "awesome-company.co.uk"
  },
  "domains": {
  }
}

The domains object should list all the domains you want to process mail for, you can host mail for multiple domains but in our case we will just host one:

{
  "settings": {
    "myhostname": "awesome-company.co.uk"
  },
  "domains": {
    "awesome-company.co.uk": [

    ]
  }
}

Each domain can have multiple accounts, which are specified in the enclosing array, let's add info@awesome-company.co.uk:

{
  "settings": {
    "myhostname": "awesome-company.co.uk"
  },
  "domains": {
    "awesome-company.co.uk": [
      {
        "email": "info@awesome-company.co.uk"
      }
    ]
  }
}

We will need a password to let us send and receive mail, this should be in a Dovecot format.

You can use a plan text password, which looks like this: {PLAIN}SuperSecure123 but for better security we will generate a SHA512-CRYPT hash instead.

Here is another easy one-liner to do this:

docker run -it --rm adaline/dockermail-core doveadm pw -s SHA512-CRYPT

You will be asked to type in a new password twice and it will produce a hash in the correct format for you:

$ docker run -it --rm adaline/dockermail-core doveadm pw -s SHA512-CRYPT
Enter new password:
Retype new password:
{SHA512-CRYPT}$6$zpOUDQBwt1NXMFZp$t1oiUoDcoNKCgFz1atkWDGTjnL2c1vuShzrGFQta8xLlw2CEPwVDWl1NDHDPK7dYrOS.Le4TvKHiXcULGUKyM0

Let's add this password to our new account in config.json:

{
  "settings": {
    "myhostname": "awesome-company.co.uk"
  },
  "domains": {
    "awesome-company.co.uk": [
      {
        "email": "info@awesome-company.co.uk",
        "password": "{SHA512-CRYPT}$6$zpOUDQBwt1NXMFZp$t1oiUoDcoNKCgFz1atkWDGTjnL2c1vuShzrGFQta8xLlw2CEPwVDWl1NDHDPK7dYrOS.Le4TvKHiXcULGUKyM0"
      }
    ]
  }
}

At this point you have everything you need to get started, but as a nice addition let's redirect mail sent to non-existent email addresses into our info@awesome-company.co.uk mailbox. To do this we need to add a "catch all" alias, which is just an email address with no account, in our case - @awesome-company.co.uk:

{
  "settings": {
    "myhostname": "awesome-company.co.uk"
  },
  "domains": {
    "awesome-company.co.uk": [
      {
        "email": "info@awesome-company.co.uk",
        "password": "{SHA512-CRYPT}$6$zpOUDQBwt1NXMFZp$t1oiUoDcoNKCgFz1atkWDGTjnL2c1vuShzrGFQta8xLlw2CEPwVDWl1NDHDPK7dYrOS.Le4TvKHiXcULGUKyM0",
        "aliases": ["@awesome-company.co.uk"]
      }
    ]
  }
}

Save this as config.json in your /opt/dockermail/settings/ folder.
That's it, we are done with configuration.

Launch

To launch and link the dockermail modules together we will use docker-compose utility you have installed earlier.
It takes a YAML file which will look like this:

core:
  image: adaline/dockermail-core
  ports:
    - "25:25"
    - "143:143"
    - "587:587"
  volumes:
    - /opt/dockermail/settings:/mail_settings
    - /opt/dockermail/mail:/vmail
  links:
   - opendkim
   - amavis
opendkim:
  image: adaline/dockermail-opendkim
  volumes:
    - /opt/dockermail/settings:/mail_settings
amavis:
  image: adaline/dockermail-amavis
  volumes:
    - /opt/dockermail/settings:/mail_settings

Let's save this file in /opt/dockermail/settings/ as docker-compose.yml.
To launch, simply run:

docker-compose -d -f /opt/dockermail/settings/docker-compose.yml up -d

Congrats, you should now have a running email server of your very own:

$ docker-compose -f /opt/dockermail/settings/docker-compose.yml up -d
Starting settings_opendkim_1...
Starting settings_amavis_1...
Starting settings_core_1...

Client setup

You will need to set up your email client with a shiny new account.

This will be an IMAP account, use your hostname for the server.
Your incoming (IMAP) port number is 143, use STARTTLS as connection security.

For outgoing (SMTP) port you can use ether the standard 25 or if your internet provider loves money more than they love you, use 587. Again connection security should be set to use STARTTLS.

You are done - time to send that all-important "Hello world" email!

Nice things to know

Although we have configured DKIM, there are a few more things you should add
to make your email stay clear of the SPAM folder.
There is a great service mail-tester.com, which will test your configuration and give you more info on what needs to be done.

To make sure your email comes back if your server is restarted, you should add
a startup script to your server's init system. Newer Ubuntu servers use systemd,
while older might use upstart - search the web on how to configure yours.


Add me on LinkedIn: Valentin Arkhipov

Follow me on Twitter: @webmadespecial


All blog posts