Mailu is a fully-featured mail server solution that runs under Docker Swarm or with Docker Compose. It automatically integrates all opensource software and includes Postfix, Dovecot, and RSpamd.

Project website is at

Installation[edit | edit source]

Installation is as simple as obtaining a docker-compose configuration file and starting the stack. Obtain a docker-compose config from:

Ensure that all domains used in the configuration are properly resolving to your server. The config also assumes that you do not have any custom reverse proxy set up (such as with Traefik) as it bundles its own 'front' proxy.

Bring up the containers, then create a new admin account:

# docker-compose up -d
# docker-compose exec admin flask mailu admin admin password

Once the containers have started, you should be able to navigate to the admin console under /admin.

Amazon Web Services[edit | edit source]

Running mailu on a EC2 instance is identical. However, there are some caveats:

  • AWS limits SMTP traffic unless you ask for an exception
  • IPs assigned from AWS's ElasticIP pool have a low reputation. Some are even blacklisted by SpamHaus
  • Using AWS SES as a mail relay works except for forwarded messages because of unverified address set in the 'From' header.

Some IPs are worse than others. You can try obtaining various Elastic IP addresses until you get one that is clean. However, mail sent is still unreliable since gmail sometimes just refuses the email from being accepted.

Instead of sending mail from your EC2 instance directly, you could try using SES as a mail relay. This gets around the unreliable delivery issue. However, emails that are forwarded won't be delivered since the sender isn't recognized by SES. You can get around this by rewriting the envelope sender to a verified address. Even better is if you can also rewrite the reply-to header with either the original reply-to address or the original sender address.

header_checks = regexp:/etc/postfix/first_header_checks
smtp_header_checks = regexp:/etc/postfix/second_header_checks
sender_canonical_maps = regexp:/etc/postfix/sender_canonical


/^To:(\s)?(.*)$/i PREPEND X-Original-To: $2
/^From:(\s)?(.*)/i  PREPEND X-Original-From: $2


/^From:([^<]*)\s?<(.*)>/i  REPLACE From: $1 ($2) <>
/^From:\s?<(.*)>/i  REPLACE From: $1 <>

While this works, it isn't a good user experience. Replying to the forwarded email will go to the forwarder address. You can try to prepend a "Reply-To" header but you risk getting two "Reply-To" headers on the forwarded message if the original email already has a "Reply-To" header (this is hard to solve because Postfix only does actions based on line-by-line if-match-then-do-something type system).

See Also:

Troubleshooting[edit | edit source]

Periodic 'Authentication Failed' Errors[edit | edit source]

Looking at the logs, you see:

front_1     | 2019/11/24 22:34:26 [info] 10#10: *216931 client login failed: "Authentication rate limit from one source exceeded" while in http auth state, client:, server:, login: ""

It looks like rainloop is causing a bunch of authentication requests for each HTTP request that is served. This then causes the authentication rate limit to hit.

The 'fix' is to just raise the limits. This however defeats the whole point of a rate limit (ie. to prevent brute force attacks).