Hosting your own email server is probably one of the most challenging things to set up. Sending a send-only email server is easy, but receiving emails and setting up mailboxes and client access is very difficult. It is necessary to configure multiple pieces and integrate them together properly to have a proper functioning Email Server. I personally tried to do it on my own (more on that later), but why reinvent the wheel? Today, we will use Mailcow to create a fully functioning email server!

What is Mailcow?

Mailcow is a fully automated piece of software which installs a fully functional mail server on your machine. I personally feel that Mailcow is one of the best options to install a fully functioning mail server. Mailcow makes use of dockers, which makes it very portable (unlike Mail-in-a-box, which is limited to Ubuntu 14.04). If you are unfamiliar with dockers, they are basically a way to package applications in containers, and these containers share the same Operating System. This is similar to how VMs share the same resources as their host using a hypervisor. Mailcow comes with an excellent web-based administration tool, but an nice-ish(my opinion) web-based client email access tool. Mailcow requires minimal configuration, and is a very quick install.

Step-by-step guide

Requirements:
I am using a fresh install of Ubuntu Server 16.04. I recommend atleast 2GB of RAM (4 if possible). If using a VM, use atleast 2 vCPUs, or atleast 1 GHz of processing power for acutal computers. Make sure to have atleast 25 GB of free space for the install and subsequent data. Your system architecture must be x86_64.

Check your ports using the following command:
sudo netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995'
If the command results in any programs, shut those programs down! It is okay if port 80 and 443 is occupied by an existing web server if you intend to reverse proxy to the Mailcow Web UI using this mail server.

  1. First you need to install Docker on your system, and enable it

     sudo apt-get install curl -y
     curl -sSL https://get.docker.com/ | CHANNEL=stable sh
     systemctl enable docker.service
     systemctl start docker.service
    
  2. Next, install Docker-Compose on your system

     curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
     chmod +x /usr/local/bin/docker-compose
    
  3. Next, clone the mailcow repository

     sudo umask 0022
     cd /opt
     git clone https://github.com/mailcow/mailcow-dockerized
     cd mailcow-dockerized
    
  4. Next, generate the configuration file using ./generate_config.sh. Use your fully-qualified domain when asked

  5. You can change the mailcow.conf file to edit the paramters.

    • If you intend to reverse proxy to the Mailcow web server using the existing web server on the system (guide to reverse-proxy below), you may want to change the HTTP_PORT and HTTPS_PORT parameters to different, unused ports. Also change SKIP_LETS_ENCRYPT=y for reverse proxying (Not necessary, but recommended).
    • I also recommend changing SYSCTL_IPV6_DISABLED=1 if you don't intend to use IPv6.
    • Change SKIP_CLAMD=y if you have RAM contraints. ClamAV can be a RAM hog.
  6. Next, start all the dockers!

     cd /opt/mailcow-dockerized
     sudo docker-compose pull
     sudo docker-compose up -d
    

You should now be able to access the web administration page at https://<domain-name> with default credentials admin and password moohoo. But wait, even the easiest mail servers need some extra work!

Ubuntu Firewall configuration

You can find a list of required ports here and what they do. If your Ubuntu machine has multiple IPs, you may want to bind your services to a particular IP based on the guide to the link. My assumption is that your machine is behind a router/firewall, and you are port forwarding to the ports you are about to open. You can obviously disable Ubuntu Firewall via sudo ufw stop and sudo ufw disable, if you control and own the entire network behind the firewall. Configuring Ubuntu Firewall is extremely important if you machine has a public IP.

ufw allow 25
ufw allow 80
ufw allow 110
ufw allow 143
ufw allow 443
ufw allow 465
ufw allow 587
ufw allow 993
ufw allow 995
ufw allow 22
DNS configuration

This is probably one of the most important aspects of configuring an email-server. Without proper DNS configuration, other SMTP servers may reject emails from your servers, or may not be able to deliver emails to your server. It might be beneficial to use a DNS service like Cloudflare to manage the DNS of your domain. While I am listing the DNS records as they would be inserted into a BIND zone file, you can use the data to enter it to any other DNS provider that you have.

First, add the A records for the domain you used for your mail server (I am using mail as an example):
mail IN A <Your IP>

Next, you should add 2 CNAME records that point to your mail A record:

autodiscover   IN   CNAME   mail
autoconfig   IN   CNAME   mail

Now, add the MX record for your domain:
@ IN MX 10 mail

Next, add the SPF record to your DNS, which ensures that only the servers with the IP of your MX records are allowed to send emails for your domain:
@ IN TXT "v=spf1 mx ~all"

Next, add the DKIM TXT record to your DNS. To get the value of this TXT record, you need to access the Mailcow administration UI and get the key. To generate the key, log into the Mailcow UI, go to Configuration -> ARC/DKIM keys. Next, generate a key for your domain. (Note: you may need to first add the domain by going to the Configuration menu at the top -> Mailboxes -> Domains tab)

After generation, the key should look like something below:
dkimkey

Add that value to the DNS:
dkim._domainkey IN TXT "value-from-UI"

Last, add the DMARC record which ensures and reassures the servers receiving emails from your domain that the email did in fact come from your server. This is a great way to combat scammers and phishers who may use your domain to spoof emails. DMARC ensures that if both the SPF and DKIM checks fail, the server on the other hand rejects or quaratines that email. Here's a neat diagram which explains how DMARC works:
dmarc-working
Source

Here's the DNS record:
_dmarc IN TXT v=DMARC1; p=reject; rua=mailto:<email-to-mail-reports-to>
Obviously, add the email where you want stats. Also, you can change the reject to quarantine if you want the server at the other end to only quarantine the email to the spam folder.

Last, you can add the following DNS records for advanced configuration. I recommend doing this. Again, email servers are complicated!

_imap._tcp          IN SRV     0 1 143   mail.example.org.
_imaps._tcp         IN SRV     0 1 993   mail.example.org.
_pop3._tcp          IN SRV     0 1 110   mail.example.org.
_pop3s._tcp         IN SRV     0 1 995   mail.example.org.
_submission._tcp    IN SRV     0 1 587   mail.example.org.
_smtps._tcp         IN SRV     0 1 465   mail.example.org.
_sieve._tcp         IN SRV     0 1 4190  mail.example.org.
_autodiscover._tcp  IN SRV     0 1 443   mail.example.org.
_carddavs._tcp      IN SRV     0 1 443   mail.example.org.
_carddavs._tcp      IN TXT     "path=/SOGo/dav/"
_caldavs._tcp       IN SRV     0 1 443   mail.example.org.
_caldavs._tcp       IN TXT     "path=/SOGo/dav/"
Domain and Mailbox configuration

Finally, you start setting up domains that you want to manage, and mailboxes.

  • Go to Configuration menu at the top -> Mailboxes
  • Add all the domains that you want to manage (and make sure you have appropriate DNS records for all of them as detailed above)
  • Create Mailboxes for each email ID you want to manage!
    You can do all sorts of fun things and tinker with quotas, settings, and more! I will let you guys figure that one out!

You access the webmail client, you can go to Apps menu at top -> SOGo. There is also a link for this webmail client on the default Mailcow page, right below the login fields. The SOGo email client looks something like this:
webmail-example
I am not the biggest fan of SOGo interface, as it can sometimes be slightly laggy, but on the other hand, it integrates address books and calendars into one easy interface, which is a HUGE plus! You can even mark certain emails as Spam or not-Spam through this client. Overall, it fits the bill VERY WELL. I may have just convinced myself to change my own opinion about the interface :P .

Few nice things about Mailcow:

  • Regular users can also log into the Mailcow UI to change passwords, and add temporary email aliases (for junk email)
  • Check out the Rspamd web interface, which is located in Configuration -> Debug -> Rspamd. The debug menu is also where you find all the logs.
  • To shut down mailcow, just do cd /opt/mailcow-dockerized, and sudo docker-compose down, and that should shut down all the dockers. To start, use sudo docker-compose up -d
  • To update Mailcow, just go to the folder using cd /opt/mailcow-dockerized, and do sudo ./update.sh. To simply check, use sudo ./update.sh --check. If you made any changes to the mailcow repository that you want to prevent from being overwritten during update, you can do ./update.sh --ours. For overwrites, use ./update.sh --theirs
  • Client configurations for various devices and apps can be accessed in the Mailcow UI

Reverse-proxy guide

I am only writing example configurations from Apache. You can probably find equivalent configuration for Nginx and other web servers online. Why is reverse-proxying useful? With reverse proxying, you can have a single web server facing the outside world on a public IP which reverse proxies to the internal network for all the services (One IP mo' problems amirite?). You can also offload all the SSL stuff on your front-end machine, which only using less processing intensive HTTP on the internal network. Reverse-proxying is also an excellent way of load-balancing services over multiple internal web servers. A reverse-proxy front-end can also act as a cache in front of the actual web server to accelerate most-accessed content.

Add a VirtualHost configuration in your Apache configuration to handle all the HTTP traffic:

<VirtualHost *:80>

ServerName <domain-name>
ServerAlias <alternate-domain-name>
ServerAdmin <your-actual-email>

Redirect permanent / https://<domain-name>/

ErrorLog ${APACHE_LOG_DIR}/error-http.log
CustomLog ${APACHE_LOG_DIR}/access-http.log combined

</VirtualHost>

Here is a sample VirtualHost config for all incoming HTTPS connections:

<VirtualHost *:443>

ServerName <domain-name>
ServerAlias <alternate-domain-name>
ServerAdmin <your-actual-email>

ProxyPreserveHost On
ProxyPass "/" "http://<IP>:<port>/"
ProxyPassReverse "/" "http://<IP>:<port>/"
ErrorLog ${APACHE_LOG_DIR}/error-ssl.log
CustomLog ${APACHE_LOG_DIR}/access-ssl.log combined

SSLEngine On

#Uncomment the line below if using Let's Encrypt
#Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile <Path-To-Certificate>
SSLCertificateKeyFile <Path-To-Private-Key>

</VirtualHost>

That's it for the reverse proxy!

Enjoy your functioning Mail Server! You deserve it after the hard work!