Introduction
I'm obsessed with self hosting services. I started self hosting things about a year ago and ever since I have loved the idea of hosting my own email server, but I always thought that it would be far too complicated and I would be stuck with large email providers like Google or Microsoft.
Enter Docker-Mailserver.
Docker-Mailserver is a full-stack email server which includes SMTP, IMAP, LDAP, Anti-spam, and Anti-virus, all wrapped in an easily configurable Docker image.
Requirements
In order to get this running you'll need somewhere to host the email server, I am using a $6 Vultr VPS running Ubuntu Linux which is plenty of power for 1 person.
On the server, you will need to have docker installed (optionally docker-compose as well, makes installation a lot easier) and root access if you're using Uncomplicated Firewall (UFW).
Lastly, you will need a domain name and the ability to edit DNS zones in order to send and receive emails on your domain.
Getting started
Setup script
If anything during this setup is unclear, Docker-Mailserver have put together some great documentation to help you out. https://docker-mailserver.github.io/docker-mailserver/edge/
Start by creating a directory to work out of in your home directory and download the setup script that we'll use to interact with the Docker-Mailserver container.
mkdir docker-mailserver
cd docker-mailserver
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh
chmod a+x ./setup.sh
Next we'll run the setup script once so that the Docker image is pulled and saved to the server: ./setup.py help
Once the image has been pulled, you should see a printout on the screen from the setup script with some example commands. You will need to use this to interact with the Docker-Mailserver container for things like debugging, adding email accounts and configuring DKIM keys.
Docker compose
The docker-compose file below is what I am using in my setup; however, there is a sample file that can be used on the docker-mailserver website plus a lot of extra environment variables that aren't listed here which can be turned on.
services:
mailserver:
image: docker.io/mailserver/docker-mailserver:latest
hostname: mail
# Change this to your domain, it will be used for your email accounts.
domainname: example.com
container_name: mailserver
ports:
- "25:25"
- "587:587"
- "465:465"
- "993:993"
- "143:143"
volumes:
- maildata:/var/mail
- mailstate:/var/mail-state
- maillogs:/var/log/mail
- ./config/:/tmp/docker-mailserver/
- ./docker-data/certbot/certs:/etc/letsencrypt
environment:
- ENABLE_SPAMASSASSIN=1
- SPAMASSASSIN_SPAM_TO_INBOX=1
# Enable this if your server has more than 1GB of RAM
- ENABLE_CLAMAV=0
- ENABLE_FAIL2BAN=1
- ENABLE_POSTGREY=1
- ENABLE_SASLAUTHD=0
- ONE_DIR=1
- DMS_DEBUG=0
- SSL_TYPE=letsencrypt
# You may want to enable this: https://docker-mailserver.github.io/docker-mailserver/edge/config/environment/#spoof_protection
# See step 8 below, which demonstrates setup with enabled/disabled
SPOOF_PROTECTION:
- SPOOF_PROTECTION=0
cap_add:
- NET_ADMIN
- SYS_PTRACE
restart: always
volumes:
maildata:
mailstate:
maillogs:
Make sure to read the comments within the Docker-compose file as there may be a few changes required for your setup.
Once you're happy with the configuration, startup the container with
docker-compose up -d
. Make sure the container started correctly by running
docker logs mailserver
.
Firewall / Port Forwarding
By default, allowing the required ports should be very simple but these steps may be slightly different depending on your setup.
For Vultr, there is no firewall configured behind the server by default so we simply need to run the following UFW commands on the server itself. If you prefer, you can disable UFW on the server and setup a firewall in the Vultr management interface instead.
ufw allow 25
ufw allow 587
ufw allow 465
ufw allow 143
ufw allow 993
ufw allow 80 # I'll explain why shortly.
The ports above will remain the same for any firewall setup.
DNS
Now that we have a running container for our mailserver and we have allowed the required ports on our server, we're ready to enable our domain to send and receive emails.
You will need to create 2 kinds of records in order to get mail flow working, an MX record to direct the mail flow and an SPF record to validate where you're allowed to send emails from.
Your DNS records should look something like this:
mail IN A *your-server-IP*
; mail-server for example.com
3600 IN MX 1 mail.example.com.
; Add SPF record
IN TXT "v=spf1 mx ~all"
Inside your SPF record, you may want to replace 'mx' with/or add 'ip4:your-ip-address', especially if you have something like an SMTP relay.
We'll also need to generate DKIM keys and create a new DNS record for that. On your mailserver host, use the setup script to run ./setup.sh config dkim
If you have copied my setup, copy the content of the file config/opendkim/keys/example.com/mail.txt
and add it as a DNS record like the MX and SPF records earlier
Example:
mail._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; " "p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgSvBJpS8a2FE2x9uwUCsNMiGUOckHQVJJQsnM3+TdWgVMUlGiCkizcZ6HODzNEzCvQxHf2Esz+kLumUBVg32lFha0UecfABD24tlLQqc449ROy70WLNe01cmzacZYc/ameV1YIzpo2xvN49ZK8bGnbzDiu2nSN5ASAexa9"
"V/MSCOB5uZigfVe0XBycH3oCGGEN7QxvU1eZ3/ea/WpNzCFp89O9o96+4k5ySgB+zfgzk/iP50f5uwKboZZT0GhLGIjD0TOAZW5SJhg4KdEboFd06xYwe0v6Ns2HIoumEhk5w9KL8uF4SKtc8lrtafGD/XgNPsoVbzvTD365FGYt0bd8xPhctdZh99rX8SYuZUXmjKACRQ9ZjdFtb1OcCbb/jK" "GAgZFLSD2qFvVOI3R0jJyFotIWOCjTqEJw9FAH8sEi75D8e1L/L2ZoJsinInkzroxSErqaDJ6nUn/D7+WfEDPIRdwIXL6PEY3rUtwikPvua6HhdiqxCTKvgZuQXMhShi0dTZpmNWe0pxoLOD+Wfadk2MF/9WHi+hJ8u5Hs6AqdxrZZNnMVScOrU2UlephG3FfMB+HNqZXFMCAwEAAQ==" ) ;
----- (Edited) DKIM key mail for gardnr.dev
Lets Encrypt Certificate
This step is optional; however, it is highly recommended.
In the docker-compose file created earlier, we set the SSL_TYPE
to letsencrypt and mounted ./docker-data/certbot/certs
to /etc/letsencrypt
. This was to setup an application called Certbot which will generate an SSL certificate for our domain automatically.
The certifcate should generate when you run the docker-compose file but if you're having trouble, the below command can be run to generate an SSL certificate in the same folder.
# Change `mail.example.com` below to your own FQDN.
# Requires access to port 80 from the internet, adjust your firewall if needed.
docker run --rm -it \
-v "${PWD}/docker-data/certbot/certs/:/etc/letsencrypt/" \
-v "${PWD}/docker-data/certbot/logs/:/var/log/letsencrypt/" \
-p 80:80 \
certbot/certbot certonly --standalone -d mail.example.com
This is why we allowed port 80 earlier
You will receive an email from letsencrypt after 30 days advising that your SSL certificate has expired so make sure you run the renew command every 30 days.
# This will need access to port 443 from the internet, adjust your firewall if needed.
docker run --rm -it \
-v "${PWD}/docker-data/certbot/certs/:/etc/letsencrypt/" \
-v "${PWD}/docker-data/certbot/logs/:/var/log/letsencrypt/" \
-p 80:80 \
-p 443:443 \
certbot/certbot renew
Wrapping up
At this stage, the mailserver is completely setup. The last thing we need to do is create an email address using the setup script:
./setup.sh email add admin@example.com passwd123
./setup.sh alias add admin@example.com postmaster@example.com
Now start your container and you're done!
This guide won't cover setting up an email client so you can send/receive emails but I found eM Client (Link) to be a really nice alternative to for Outlook and it can be used completely free. eM Client is also very easy to configure as you just need to plug in your mailserver address and the secure SMTP & IMAP ports.
Links
Website: https://gardnr.dev/
GitHub: https://github.com/Jay-Gardiner/
Docker-Mailserver: https://docker-mailserver.github.io/docker-mailserver/edge/
eM Client: https://www.emclient.com/