Everyone should have their own domain name (or several). Having a website on your domain is easy and a sensible use of that domain name. Almost no one should run their own email server, it’s complicated and makes you responsible for all of the problems.
There are lots of providers out there that run email services and allow you to bring your own domain. ProtonMail would be a good choice, you can even bring your own custom domain and still use ProtonMail. Alternatives are offered by Google, and Microsoft if you want to go that route, both provide support for custom domains.
If you are still thinking of running your own mail server, then grab your tinfoil hat and let’s look at the best way to operate a mail server in the age of containers. I’ve run my own email for a long time, mostly following the Ubuntu setup. Using the docker-mailserver solves all of the email problems with a single container.
I will mention there are many alternatives: mailu, iredmail, etc. The docker-mailserver project stands out for me as they have avoided database based configuration and have stuck with ‘files on disk’ as the model.
This is a long overdue mail upgrade. I started doing this way back in 2017, but never really finished the work. The SSD rebuild disrupted how I was doing things, and changing email is a little scary. The hard drive that stores my email is very old. It is a Seagate Barracuda 40GB (ST340014A). The SMART information says that the Power Cycle Count is only 502, but the Power On Hours is an astounding 130442 (that is 14.89 years). Every stat is in pre-fail or old age, it is definitely time to move my email off that drive.
Before starting, take the time to read through the documentation. Once you think you’re ready to start installing thing the ReadMe is the right path to follow. I’m going a slightly different path than the recommended one which uses docker-compose, and will build out a basic docker deployment.
First I grabbed the following two files
1 2 |
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/docker-compose.yml wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/mailserver.env |
And the setup script for v10.0.0 as I intend to use the ‘stable’ version vs. ‘edge’. It is important to get the matching setup.sh script for the version you are deploying.
1 2 |
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/v10.0.0/setup.sh chmod a+x ./setup.sh |
I used the docker-compose.yml file to inform me how to configure my Makefile based docker approach. Most of the create options are a direct mimic of the compose file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# # email # https://hub.docker.com/r/mailserver/docker-mailserver # NAME = mailserver REPO = docker.io/mailserver/docker-mailserver:latest # ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) # Create the container build: docker create \ --name=$(NAME) \ --hostname mail \ --domainname lowtek.ca \ -p 25:25 \ -p 143:143 \ -p 465:465 \ -p 587:587 \ -p 993:993 \ -v /path/to/mail/maildata:/var/mail \ -v /path/to/mail/mailstate:/var/mail-state \ -v /path/to/mail/maillogs:/var/log/mail \ -v /etc/localtime:/etc/localtime:ro \ -v $(ROOT_DIR)/config/:/tmp/docker-mailserver/ \ -v /path/to/etc/letsencrypt:/etc/letsencrypt \ --env-file $(ROOT_DIR)/mailserver.env \ --cap-add NET_ADMIN \ --cap-add SYS_PTRACE \ --restart=unless-stopped \ $(REPO) # Start the container start: docker start $(NAME) # Update the container update: docker pull $(REPO) - docker rm $(NAME)-old docker rename $(NAME) $(NAME)-old make build docker stop $(NAME)-old make start |
I walked through the mailserver.env
file and made a few changes
ONE_DIR=1
I’m not totally sure about this, but the documentation reads: “consolidate all states into a single directory (/var/mail-state
) to allow persistence using docker volumes.” which seems like a good idea.ENABLE_CLAMAV=1
My existing email server uses ClamAVENABLE_FAIL2BAN=1
I’m a fan of fail2ban for protecting my server from abuseENABLE_SPAMASSASSIN=1
My existing email sever uses SpamAssassin
The volume pointing to letsencrypt is sort of a placeholder for now. Once we get things basically setup I will be changing the SSL_TYPE to enable encryption using my existing letsencrypt certificate that my webserver container has setup.
I later added the following additional configuration to enable logwatch.
-
LOGWATCH_INTERVAL=daily
Having an email centric logwatch email vs. combining it with my server logwatch, seemed like a good idea. -
LOGWATCH_RECIPIENT=postmaster@lowtek.ca
Where to send the email.
With my Makefile based docker approach I have build
, start
and update
targets. I can manually roll-back if needed as the previous container is has a -old
name. The first step is to build the container.
1 |
make build |
At this point we are at the Get up and running section. We need to start the container and configure some email addresses.
1 |
make start |
Assuming all goes well, the mailserver container will be running. If we poke around the filesystem we’ll see a few files have been created
config/dovecot-quotas.cf
maillogs/clamav.log
maillogs/freshclam.log
We should be able to run the setup script and add a user, and configure some key aliases.
1 2 3 4 5 |
# Put a space in front of the command # This will prevent the shell from logging your password ./setup.sh email add myemail@lowtek.ca SuperSecretPassword ./setup.sh alias add postmaster@lowtek.ca myemail@lowtek.ca |
The creation of the account will cause some additional files to be created
config/postfix-accounts.cf
config/postfix-virtual.cf
At this point we have a running email server – but we need to start getting data to flow there. You may have to open ports in your firewall on the docker host to allow external traffic to connect to this new container.
This is very encouraging, but there is still a list of things I need to do
- Create accounts and aliases
- Configure smart host sending via AWS SES
- Enable SSL_TYPE
- Set up DKIM
- Change the port forwarding to point to my container host
- Migrate email using imapsync
The rest of this post is details on those steps