A simple mail stack for self-hosting your email.

Simon Detheridge 09907aa42b Add some documentation 3 years ago
bin c4731279e8 Initial mailstack implementation 3 years ago
clamav c4731279e8 Initial mailstack implementation 3 years ago
db c4731279e8 Initial mailstack implementation 3 years ago
dkim c64aa2d007 Move dkim folder to project root for ease-of-use 3 years ago
dovecot c4731279e8 Initial mailstack implementation 3 years ago
haraka c64aa2d007 Move dkim folder to project root for ease-of-use 3 years ago
proxy c4731279e8 Initial mailstack implementation 3 years ago
rainloop c4731279e8 Initial mailstack implementation 3 years ago
redis c4731279e8 Initial mailstack implementation 3 years ago
rspamd c4731279e8 Initial mailstack implementation 3 years ago
README.md 09907aa42b Add some documentation 3 years ago
docker-compose.yml c4731279e8 Initial mailstack implementation 3 years ago


Simple Self-Hosted Mail

A quick way to host your own mail.


This is a docker-compose-based environment for setting up a personal mailserver as quickly and simply as possible. Included are a collection of components, bundled together via a compose file, which provide a simple but fully-functional mail stack.

It supports:

  • Multiple users
  • Multiple domains
  • Webmail
  • Antispam
  • DKIM/SPF/DMARC to ensure good outgoing delivery rates
  • SSL
  • Server-side mail filtering

The framework is built from the following components:

  • Haraka message transfer agent (SMTP)
  • Dovecot mail delivery agent (IMAP) and filtering (Sieve)
  • Rainloop moden web-mail interface
  • Rspamd spam filtering system
  • MariaDB database for simple user management
  • ClamAV anti-virus
  • Nginx reverse proxy for Rainloop
  • Redis backend for Rspamd

The default configuration is fairly minimal but will be enough to get started straight out of the box, but can be tweaked by modifying the config files in the repository.

Getting Started


You will need:

  • A domain, and a way to edit the DNS. Cloudflare works well.
  • A Linux server, with Docker installed.
  • IMAP[S], SMTP[S] and HTTP[S] ports free
  • SSL certificates from Let's Encrypt via Certbot


The configuration expects you to have SSL certificates, and will look for them in Certbot's default directory. (/etc/letsencrypt/)

An example of using certbot to obtain a certificate using your Cloudflare account:

docker run -ti --rm -v /etc/letsencrypt:/etc/letsencrypt \
  certbot/dns-cloudflare \
    certonly --dns-cloudflare \
    --dns-cloudflare-credentials /etc/letsencrypt/renewal/cloudflare.ini \
    -d mail.MYDOMAIN.XYZ

This assumes that you have written your cloudflare credentials to /etc/letsencrypt/renewal/cloudflare.ini - more information is available on this and other ways of getting free SSL certificates in the documtation for Certbot


Clone the repository

git clone https://git.sd.ai/simon/simple-selfhosted-mail

All following comands are relative to the root directory of the repository.

DKIM keys

You'll want to generate a DKIM key for your domain, as follows:

cd dkim
./dkim_gen_key.sh MYDOMAIN.XYZ


The file MYDOMAIN.XYZ/dns contains the DNS records you need to add to your domain for SPF, DKIM and DMARC.


In your DNS, you will need to add:

  • An A record for your server's public IP. e.g. mail.MYDOMAIN.XYZ
  • An MX record for your domain, pointing at your A record.
  • The DNS records from the file above. Specifically:
    • The monthYYYY._domainkey TXT record for DKIM
    • The TXT and SPF records in the root of your domain for SPF
    • The _dmarc TXT record containing your DMARC record

Important: The reverse DNS for your IP should match the A record you want to use. Without this, you will look spammy to other mail servers and may experience delivery problems.

The entries generated in the dns file should work without modification, and should be added as-is unless you know what you are doing.

Environment variables

You need three environment variables set before you bring up the mail stack:

  • MAIL_HOSTNAME should be the hostname of your mail server, and should match your reverse DNS
  • SSL_DOMAIN is the name of the directory in /etc/letsencrypt/live that contains your SSL certificate.
    • Normally this will match MAIL_HOSTNAME if your certificate just has one host, but if you have multiple hosts in the same cert then it may be something different.
  • MYSQL_PASSWORD is the password used to initialise and connect to the MariaDB database. (The username will be dovecot)
    • Once you have brought the stack up for the first time, you'll want to keep this the same. To change it, you will need to connect to the database directly and modify it as the root user.
    • This can be anything you like, but it's best to randomly generate it.

The easiest way to set these up is to add them to your ~/.bashrc. e.g.:

export MYSQL_PASSWORD=some_secure_password

Don't forget to reload your .bashrc when done:

. ~/.bashrc

Building the stack

The docker-compose build command will download all of the necessary base images and configure them. Run this inside your repository.

You can then bring everything up with docker-compose up -d

Creating your first user

There are user-management scripts in the bin subdirectory, which call docker-compose so should be run from the repository root. These commands are fairly self-explanatory:

  • list_users
  • add_user
  • set_user_password
  • delete_user

To create the first user you'll want to run:

bin/add_user me@MYDOMAIN.XYZ

You will be prompted for a password.

Configuring Rainloop

Rainloop webmail should be listening on your mail server now. You will need to set up your domain via the admin interface before you can log in:

  • Visit: https://mail.YOURDOMAIN.XYZ/?admin
    • username: admin
    • password: 12345
  • Go to security and change the admin password!
  • Go to domains and add your domain:
    • Click Add Domain
    • Enter YOURDOMAIN.XYZ under Name
    • In the IMAP section:
    • Enter dovecot for Server
    • Select STARTTLS for Secure
    • Enter 10143 for Port
    • In the SMTP section:
    • Enter haraka for Server
    • Select STARTTLS for Secure
    • Enter 2525 for Port
    • Check Use authentication
    • Click Test - it should say everything is OK
    • Click Sieve configuration
    • Check Allow sieve scripts and Allow custom user script
    • Enter dovecot for Server
    • Leave 4190 for Port
    • For Secure set STARTTLS
    • Click Test again - it should test the sieve configuration and verify that it is OK
    • Click Add

TODO: Add some screenshots!

NOTE: The internal ports specified above are different to the ports externally published, which are the standard IMAP and SMTP ports. The internal ports are on numbers >1024 so that the processes can be run as a non-root user.

This will have configured Rainloop to handle your domain, so that it knows how to send and receive mail from the rest of the stack.

Logging in

Visit https://mail.MYDOMAIN.XYZ/ and log in as the user you created earlier.

You may want to use the DKIM tester at (http://www.appmaildev.com/en/dkim) to verify that your setup is correctly signing messages.


To update and rebuild the stack, run:

git pull
docker-compose build --pull

Important information and potential gotchas

There are a couple of things that it helps to be aware of:

Where the data is stored

Your mail and settings are stored in Docker volumes defined in docker-compose.yml. Please be careful when running commands such as docker-compose down (don't run it with the -v parameter) or docker prune as these may delete volumes, which will wipe out your mail!

Errors about volumes being already mounted

If you rebuild any of the containers in the stack and then re-run docker-compose up, you may see errors about volumes already being mounted. When you rebuild, you must run docker-compose down before bringing it back up again.

'Permission denied' errors for SSL certificates

It may be that the programs running inside your containers can't read your SSL certificates, due to the fact that they run as their own user (e.g. users dovecot and haraka.) The SSL certificates are bind-mounted into the running containers and inherit the permissions that they have on disk.

You may need to change the permissions on the files to support this. If you are confident that only you have access to the server, running chmod a+r /etc/letsencrypt/archive/mail.YOURDOMAIN.XYZ/* will work, but be aware of the security implications of doing this.

Multiple domains

Multiple domains are supported. Simply add another user with bin/add_user and


Feel free to make a PR or open issues. Feedback is good.