Post

Self-Hosted Domain Email Part 2: Mailu Installation and Hardening

Learn how to install and secure your own multi-domain email server using Mailu, Docker, and fail2ban. This step-by-step guide walks you through VPS selection, configuration generation, firewall setup, and security hardening to create a cost-effective alternative to commercial email hosting.

Self-Hosted Domain Email Part 2: Mailu Installation and Hardening

Overview

In Part 1, we explored why email is surprisingly complex despite its deceptively simple beginnings. Now it's time to roll up our sleeves and build our own multi-domain email server using Mailu, a fantastic Dockerized mail server suite.

This guide assumes you're an IT hobbyist with multiple domains who wants to avoid the typical $5/month/domain costs of commercial email hosting. By the end of this post, you'll have set up a secure, functional mail server that can handle multiple domains with proper security hardening.

TL;DR

This guide walks you through setting up Mailu on a VPS, configuring it securely with Docker, hardening it with fail2ban and a proper firewall, and setting up the necessary DNS records. You'll end up with a multi-domain email server at a small recurring monthly cost for unlimited* domains and unlimited* users within each domain. This includes webmail access and admin interfaces.

Heads up: This post contains some affiliate links. If you buy something through them, I might earn a small commission — at no extra cost to you. I only recommend tools I've actually used or vetted.

Prerequisites

Before we start, you'll need:

  1. A domain name you want to use for email (we'll use example.com throughout this guide)
  2. Basic familiarity with Linux command line and SSH
  3. Access to manage your domain's DNS records (we'll use Cloudflare in this guide)
  4. A few hours of time (most of which is waiting for DNS to propagate)

1: Choosing the Right VPS

Not all VPS providers are suitable for hosting email. The provider must:

  1. Allow outbound traffic on port 25 (many cloud providers block this)
  2. Provide IPs with good reputation (not previously used for spam)
  3. Allow you to set reverse DNS (PTR) records

Once you've purchased your VPS, make sure to:

  1. Set a strong root password
  2. Note your server's IP address
  3. Request a PTR record setup (contact support to set mail.yourdomain.com as the reverse DNS for your IP)

DNS PTR Records: When you send an e-mail, it will have the host that sent the message, but also the IP address. So, part of the anti-spam effort is to do a reverse DNS lookup on the IP address to see if it matches the host that sent the message. And more specifically if it matches the DNS SPF record. That is, if the IP address is allowed to send e-mail for that domain.

Meaning, if you don't have a PTR record set up, your email will be marked as spam by most providers. This is because it looks like an unauthorized third party is sending email from your domain.

Most VPS providers allow you to set your reverse DNS through their control panel or by submitting a support ticket. This is critical for email deliverability - don't skip it!

1.1: Big Tech IaaS Providers

If you need a mail server setup for a legitimate business, you probably want to use a Big Tech IaaS provider. However, there are limited options:

Provider Email Ports Blocked? Unblock Possible?
AWS ❌ Yes ✅ Yes (via request)
Azure ❌ Yes ⚠️ No (Enterprise customers only)
Google Cloud ❌ Yes ❌ No
Oracle Cloud ❌ Yes ❌ No
Alibaba Cloud ❌ Yes ❌ No
IBM Cloud ❌ Yes ❌ No

1.2: Small IaaS Providers

Many of the smaller providers also block port 25 and other email ports by default, but you can get them unblocked by just opening a ticket with the support team.

This also is a mixed blessing. On the one hand, you can host your mail server. On the other hand, these are often the same services that spammers use, which means your IP address may be on a blocklist. This can lead to your emails being marked as spam or rejected outright.

Provider Email Ports Blocked? Unblock Possible?
DigitalOcean ❌ Yes ❌ No
Vultr ❌ Yes ❌ No
Linode ❌ Yes ✅ Yes (via request)
Heroku ❌ Yes ❌ No
RackNerd ✅ No ✅ N/A
BuyVM ❌ Yes ✅ Yes (via request)
Hetzner ❌ Yes ✅ Yes (after verification)
UpCloud ❌ Yes ✅ Yes (via request)

1.3: Example VPS Setup

I've had good experience with Racknerd, which offers affordable VPS plans that you can easily use for email. For a multi-domain email server, I recommend something like this:

  • 4GB RAM
  • 2-4 CPU cores
  • 80-100GB SSD storage
  • Debian or Ubuntu OS

WARNING: Hopefully you can see what a closed system internet e-mail is. Basically, the main email providers own everyone's email, and if you want to host your own, all of the cloud providers make it pretty unwelcoming.

With that said, although Racknerd is a very good, very inexpensive VPS provider, they have entire subnets that are on blocklists. You can request a new IP address, but that may not solve the issue. So, for my hobbyist domain emails, I can send e-mails to ProtonMail, external companies and also Gmail, but at the moment Outlook rejects all of my emails for example.

Again, if you need this for a business, you should probably use a Big Tech IaaS provider. Over time, as my IP address and domain names get more reputation, I will eventually be able to send emails to Outlook and everyone else.

2: Basic Server Setup

We'll start with a clean Debian/Ubuntu server and prepare it for hosting our mail services.

2.1: Connect to Your Server

First, connect to your server via SSH:

1
ssh root@your_server_ip

The credentials will be either available via your VPS provider dashboard or they may send you the initial password in your welcome email.

2.2: Follow the Basic Setup Guide

Rather than repeating the entire server setup process, follow this guide: Setting up a Public VPS which will get you into a decent starting place. Not a fully-hardened server, but definitely better than default. This guide covers:

  • Creating a non-root user with sudo privileges
  • Basic security configurations
  • Setting up SSH key authentication
  • Installing essential packages
  • Configuring automatic updates

At minimum, make sure you've:

  • Created a non-root user with sudo
  • Set up SSH key authentication
  • Configured basic firewall with UFW (we'll add more rules later)
  • Installed Docker and Docker Compose

3: Planning Your Server Name Structure

Before generating our Mailu configuration, we need to decide on our server naming scheme.

3.1: Domain Structure

You'll want at least two subdomains for your mail server:

  • mail.example.com - Main mail server hostname
    • Used for SMTP, IMAP, and POP3
    • Email clients will use autodiscover and autoconfig, which are DNS CNAME records for this hostname to automatically configure email clients
  • webmail.example.com - Webmail and admin interface

These subdomains will be used in your DNS configuration and for accessing your mail services. The current version of Mailu will handle certificate generation automatically, so we don't need to set up certificates manually.

Make sure to decide on your domain structure before proceeding to the configuration step. Using consistent and descriptive subdomains will help with organization and access control.

3.1.1: Create DNS Records

Create DNS records for these hostnames in your DNS provider's control panel:

  1. An A record for mail.example.com pointing to your server's public IP.
  2. A CNAME record for webmail.example.com pointing to mail.example.com.

Cloudflare Note: If you are using Cloudflare, make sure to set the DNS record for mail.example.com to DNS only (grey cloud) and not proxied (orange cloud). Email services require direct access to your server. Since webmail.example.com will only be used for the webmail interface, you can keep it proxied (orange cloud) for added security.

4: Generate Your Mailu Configuration

Mailu provides a fantastic setup tool at setup.mailu.io that will generate the necessary configuration files.

4.1: Generate Config

Visit setup.mailu.io to access the configuration tool.

4.2: Complete the setup form

Fill in the setup form with your specific details. The Mailu configuration wizard is divided into several steps:

  • Step 1 - Initial configuration
    • Version: Select the latest stable release (2024.06 as shown in the screenshot)
    • Mailu storage path: Set to /opt/mailu
    • Main mail domain and server display name: Your primary domain (e.g., example.com)
    • Postmaster local part: Usually admin (this creates [email protected])
    • TLS certificate handling: Choose letsencrypt for automatic certificate management
    • Authentication rate limits: Set appropriate limits (5 attempts per hour is the default)
    • Website name: Can leave as "Mailu" or customize
    • Admin UI: Make sure it's enabled
  • Step 2 - Pick some features
    • Webmail client: Choose "Roundcube" (more user-friendly) or "Rainloop"
    • Antivirus: Enable ClamAV to scan for viruses in emails
    • Webdav service: Enable if you need calendar/contacts sync
    • Email client setup: Enable automatic email client configuration
  • Step 3 - Expose Mailu to the world
    • Public hostnames: Set your public-facing domains:
      • Main hostname: mail.example.com
      • Webmail hostname: webmail.example.com
      • Autodiscover hostname: autodiscover.example.com
    • IPv4 listen address: Your server's public IP address
    • Subnet of the docker network: Usually keep the default

The screenshot shows the configuration interface with these options filled in for the fictional example.com domain.

As a reference, here is a realistic example for the fictional example.com domain:

Mailu Setup Example

Once you click "Setup Mailu", it will give you a summary of your specific configuration (note the GUID in the URL). Here is a realistic example of what it shows you:

Mailu Setup Instructions

4.3: Implement the Configuration

As you can see from the example, above, you'll have a summary of configuration that is specific to your mail server. Just follow those instructions.

4.4: Modify the Configuration (If Needed)

If you need to adjust any settings, edit the mailu.env file:

1
sudo nano /opt/mailu/mailu.env

Note: The mailu.env file contains all the configuration settings for your Mailu instance. Many of the settings need to be adjusted by re-running the setup tool. There will be a comment in the file indicating which settings are auto-generated and which can be modified.

You may want to update these settings:

  • Confirm your domains and hostnames are correct
  • Check that the TLS configuration is set to letsencrypt
  • Verify storage paths and resource limits

Save and exit the editor when you're satisfied with the configuration.

4.5: Working with Docker Compose

While you are in the /opt/mailu directory, you work with that service stack with a few commands. Here are the most common ones:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Start the Mailu stack
sudo docker compose up -d

# Stop the Mailu stack
sudo docker compose down

# View the logs for all services
sudo docker compose logs

# View the logs for a specific service
sudo docker compose logs <service_name>

# View the status of all services
sudo docker compose ps

Be Patient!: When you run docker compose up not only can it take a minute to pull down the various container images, it also takes a minute for all of the services to stand up. So give it a minute or up to two minutes before the web interface is available.

You can run docker compose logs -f to "follow" the logs. In other words watch all of the container logs as they are being generated. This is a good way to see if there are any errors or issues with the containers. Use CTRL+C to stop the logs.

5: Configure Firewall

In the Setting up a Public VPS guide, we set up a basic firewall using Uncomplicated Firewall (UFW) and opened just a few ports (e.g. 22, 80, and 443). Now we need to add specific rules for Mailu.

Email servers require several ports to be open. Here's how to configure UFW:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# SMTP (both incoming and outgoing mail) and Sieve
sudo ufw allow 25/tcp
sudo ufw allow 4190/tcp 

# Submission ports (for email clients to send mail)
sudo ufw allow 587/tcp
sudo ufw allow 465/tcp

# IMAP and POP3 (for email clients to receive mail)
sudo ufw allow 143/tcp
sudo ufw allow 993/tcp
sudo ufw allow 110/tcp
sudo ufw allow 995/tcp

# HTTP(S) for webmail and administration
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable the firewall if not already enabled
sudo ufw enable

Verify that the rules were added correctly:

1
sudo ufw status verbose

6: Set Up Fail2ban for Security

Fail2ban helps protect your server from brute force attacks by temporarily banning IP addresses after too many failed attempts.

Important: By default, Docker containers log messages internally. To use Fail2Ban, it needs log files to monitor. This is a little bit involved, so there is a separate post just for that. Please do not skip this step as it's critical for security. Please read:

Exposing Docker Compose Logs for IDS/IPS

In that post, that has some Mailu examples. However below, is the complete implementation that we need. So, assuming you've already read and are ready to implement that, here are the rest of the details for our specific Mailu setup.

6.1: Configure Directory for Logs

Create a directory for the logs:

1
2
3
sudo mkdir -p /opt/mailu/log
sudo chown 991:991 /opt/mailu/log   # 991 is often used by Mailu containers (e.g., for postfix/dovecot)
sudo chmod 755 /opt/mailu/log

6.2: Configure Docker Compose Logging

Edit the existing /opt/mailu/docker-compose.yml file to change the logging sections for every service to the following, and to map the volumes mapping for the log directory:

1
2
3
4
5
6
7
    logging:
      driver: syslog
      options:
        syslog-address: "unixgram:///dev/log"
        tag: "mailu-smtp"
    volumes:
      - /dev/log:/dev/log:ro

The tag should match the service name. For example, for the postfix service, it should be mailu-postfix. For the dovecot service, it should be mailu-dovecot, and so on. Or you could name them by function, like mailu-smtp, mailu-imap, etc.

6.3: Create Syslog Router

Create a new file for the syslog router:

1
sudo nano /etc/rsyslog.d/mailu.conf

And that should have the following, assuming you are using the mailu tag and that your logs will be in /opt/mailu/log:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
###############################################################################
# /etc/rsyslog.d/mailu.conf
#
# Dynamically route any SYSLOG_IDENTIFIER starting with "mailu-" into
# /opt/mailu/logs/mailu-<service>.log
###############################################################################

# Use the built-in file format (optional, but recommended)
$ActionFileDefaultTemplate RSYSLOG_FileFormat

# Define a template that writes to /opt/mailu/logs/<programname>.log
$template MailuLogFile,"/opt/mailu/logs/%programname%.log"

# If the program name begins with "mailu-", write to the dynamic file
if ($programname startswith 'mailu-') then -?MailuLogFile
& stop

6.4: Restart Everything

Then restart rsyslog to apply the changes:

1
sudo systemctl restart rsyslog.service

And restart the Mailu stack to apply the new logging configuration:

1
2
3
cd /opt/mailu
sudo docker compose down
sudo docker compose up -d

You should now see logs being written to /opt/mailu/logs/<service>.log for each service. For example:

1
2
3
4
5
6
7
8
9
10
11
-rw-r----- 1 syslog adm  4400 May 10 16:11 mailu-admin.log
-rw-r----- 1 syslog adm 73348 May 10 16:16 mailu-antispam.log
-rw-r----- 1 syslog adm  1573 May 10 16:10 mailu-antivirus.log
-rw-r----- 1 syslog adm 30091 May 10 16:11 mailu-front.log
-rw-r----- 1 syslog adm     0 May 10 14:06 mailu-imap.log
-rw-r----- 1 syslog adm     0 May 10 14:06 mailu-oletools.log
-rw-r----- 1 syslog adm  1270 May 10 15:40 mailu-redis.log
-rw-r----- 1 syslog adm     0 May 10 14:06 mailu-resolver.log
-rw-r----- 1 syslog adm     0 May 10 14:06 mailu-smtp.log
-rw-r----- 1 syslog adm     0 May 10 14:06 mailu-tika.log
-rw-r----- 1 syslog adm     0 May 10 14:06 mailu-webmail.log

6.5: Create Fail2Ban Filters

A Fail2Ban filter is a set of rules that defines what to look for in the logs. We need to create a filter for each service to define what constitutes a failed login attempt.

6.5.1: Filter for Mailu Web Interface

There are several existing filters in place for the other mail services, but we need to create a custom filter for the Mailu web interface Create a filter for Mailu Web Interface (used by the admin and webmail interfaces):

1
sudo nano /etc/fail2ban/filter.d/mailu-web.conf

Add the following content:

1
2
3
[Definition]
failregex = ^.*Failed login for .* from <HOST>.*$
ignoreregex =

6.6: Create a Fail2ban Configuration for Mailu

Create a custom jail configuration for Postfix and Dovecot:

1
sudo nano /etc/fail2ban/jail.d/mailu.conf

Add the following configuration:

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
[postfix]
enabled = true
port = smtp,submission,465
filter = postfix
logpath = /opt/mailu/log/postfix.log
maxretry = 5
findtime = 600
bantime = 3600

[dovecot]
enabled = true
port = imap,imaps,pop3,pop3s
filter = dovecot
logpath = /opt/mailu/log/dovecot.log
maxretry = 5
findtime = 600
bantime = 3600

[roundcube-auth]
enabled = true
port = http,https
filter = roundcube-auth
logpath = /opt/mailu/log/roundcube.log
maxretry = 5
findtime = 600
bantime = 3600

[mailu-admin]
enabled = true
filter = mailu-web
port = http,https
logpath = /opt/mailu/log/admin.log
maxretry = 5
findtime = 600
bantime = 3600

[mailu-front]
enabled = true
filter = mailu-web
port = http,https
logpath = /opt/mailu/log/nginx/access.log
maxretry = 5
findtime = 600
bantime = 3600

6.7: Restart Fail2ban to Apply Changes

1
sudo systemctl restart fail2ban

You can also check the status of Fail2ban to ensure it's running correctly:

1
sudo systemctl status fail2ban

If it can't find the log files that the config points it, the service will fail to start.

6.8: Implement Log Rotation

Again, I'm just recapping what is over in that other post

To prevent the logs from growing indefinitely, we need to set up log rotation. Create a new logrotate configuration file:

1
sudo nano /etc/logrotate.d/mailu-logs

Add the following configuration:

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
/opt/mailu/logs/mailu-*.log {
    # Rotate the log files every day
    daily

    # Keep 14 rotated logs before deleting the oldest
    rotate 14

    # Compress old log files to save space
    compress

    # Skip compression for the most recent rotated log; compress it on the next run
    delaycompress

    # If a log file is missing, go on without error
    missingok

    # Do not rotate the file if it is empty
    notifempty

    # After copying the current log, truncate it in place so the service can continue writing
    copytruncate

    # After rotation, create a new log file with these permissions and ownership
    create 640 syslog adm
}

6.8.1: Restart and Test Log Rotation

Restart the logrotate service:

1
sudo systemctl restart logrotate.service

You can test the log rotation configuration by running:

1
sudo logrotate -f /etc/logrotate.d/mailu-logs

This will force logrotate to run and apply the configuration. Check the /opt/mailu/logs/ directory to see if the logs have been rotated correctly. If you had a mailu-smtp.log file before, you should now see a mailu-smtp.log.1 file and a new mailu-smtp.log file.

7: Start Mailu

If you haven't already, now we're ready to start our mail server:

1
2
cd /opt/mailu
sudo docker compose up -d

This will pull all the necessary Docker images and start the Mailu services. The first startup may take several minutes as the images are downloaded and initialized.

7.1: Check the Status

Verify that all containers are running:

1
sudo docker compose ps

All services should be "Up" and healthy. If any service shows an error, check its logs:

1
sudo docker compose logs <service_name>

8: Configure DNS Records

You should now be able to access the Mailu web interface at https://webmail.example.com. However, before you can send or receive emails, you need to configure your DNS records.

Tip: You can create your admin account using the directions from the Mailu setup tool. Note that you would change the domain name and the admin name to whatever you want. Choose a very strong password and make sure to save it somewhere safe.

Create Mailu Admin

In the left navigation if you go into "Mail Domains", click on the Details icon for your domain. That will bring you to a URL like this: https://webmail.example.com/admin/domain/details/example.com. That will show you a screen like this where you should generate your keys, and then Download Zonefile (which you will need for DNS).

Log in to your DNS provider's control panel, select your domain, and navigate to the DNS records section. Providers like Cloudflare let you "Import" a native Bind-format zonefile like this, which makes this super simple.

8.1: Using the Downloaded Zonefile

This zonefile contains all the necessary DNS records formatted for easy implementation. The file will look something like this:

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
; Zone file for example.com
; Mailu managed zone

; A records for the server
mail                  IN A      203.0.113.1
webmail               IN A      203.0.113.1

; MX records for mail delivery
@                     IN MX     10 mail.example.com.

; SPF record for sender validation
@                     IN TXT    "v=spf1 mx -all"

; DKIM record for cryptographic authentication
dkim._domainkey       IN TXT    "v=DKIM1; k=rsa; p=MIIBIjANB..."

; DMARC record for reporting and policy
_dmarc                IN TXT    "v=DMARC1; p=reject; rua=mailto:[email protected]"

; SRV records for client auto-discovery
_imap._tcp            IN SRV    0 1 143 mail.example.com.
_imaps._tcp           IN SRV    0 1 993 mail.example.com.
_submission._tcp      IN SRV    0 1 587 mail.example.com.
_submissions._tcp     IN SRV    0 1 465 mail.example.com.

; Auto-discovery for mail clients
autoconfig            IN CNAME  mail.example.com.
autodiscover          IN CNAME  mail.example.com.

If you can't import the file, you will have to manually add each of these records to your DNS provider.

8.2: Essential DNS Records To Add

If you prefer to add the records manually, here are the essential records you need:

  • A records:
    • mail.example.com pointing to your server IP
    • webmail.example.com pointing to your server IP
  • MX record:
    • Priority: 10
    • Target: mail.example.com
  • SPF record (TXT):
    • Host: @ (root domain)
    • Value: v=spf1 mx -all
  • DKIM record (TXT):
    • Host: dkim._domainkey
    • Value: (copy from the zonefile, it's a long string)
  • DMARC record (TXT):
  • SRV records for client auto-discovery:
    • Service: _imap._tcp Priority: 0, Weight: 1, Port: 143, Target: mail.example.com
    • Service: _imaps._tcp Priority: 0, Weight: 1, Port: 993, Target: mail.example.com
    • Service: _submission._tcp Priority: 0, Weight: 1, Port: 587, Target: mail.example.com
    • Service: _submissions._tcp Priority: 0, Weight: 1, Port: 465, Target: mail.example.com
  • Autoconfig/Autodiscover (CNAME):
    • autoconfig pointing to mail.example.com
    • autodiscover pointing to mail.example.com

Critical: Make sure to set all email-related DNS records as DNS only (grey cloud in Cloudflare) and NOT proxied (orange cloud). Meaning, if your DNS provider offers Web Application Firewall (WAF) protections, those should be disabled for email. Email requires direct connection to your server.

9: Test Your Installation

Once all services are running and DNS records have propagated (which can take up to 24-48 hours), let's test our installation:

9.1: Web Admin Interface

  1. Visit https://webmail.example.com/admin
  2. Log in with the admin credentials you set during configuration
  3. Explore the admin interface and verify you can access all features

By default, it will have created a mail domain for mail.example.com. However, you probably want something like [email protected]. So, navigate into "Mail Domains" and add that domain. Once that domain is created you can add users from there.

9.2: Create a Test User

In the admin interface:

  1. Navigate to "Users"
  2. Click "Add user"
  3. Create a test user with your desired username and password

9.3: Test Email Reception

Send an email from an external account (like Gmail) to your new email address (e.g., [email protected]). Then check if it was received by:

  1. Visiting https://webmail.example.com/ and logging in with your test account
  2. Verifying that the email appears in the inbox

9.4: Test Email Sending

Try sending an email from your new account to an external address (like your Gmail). Check if:

  1. The email arrives
  2. It passes SPF, DKIM, and DMARC checks (this will be visible in Gmail by checking the email headers)
  3. It doesn't end up in spam

9.5: Test DNS Configuration

Use the following services to verify your DNS setup:

  • MX Toolbox - Check MX, SPF, DKIM, and blacklist status
  • mail-tester.com - Send an email and get a detailed deliverability report

10: Server Hardening and Maintenance

With your mail server running, consider these additional hardening steps:

10.1: Set Up Regular Backups

Create a simple backup script:

1
sudo nano /etc/cron.daily/backup-mailu

Add the following script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
BACKUP_DIR="/var/backups/mailu"
DATE=$(date +%Y%m%d)

# Create backup directory if it doesn't exist
mkdir -p $BACKUP_DIR

# Stop Mailu temporarily for consistent backup
cd /opt/mailu
docker compose down

# Backup the data directory
tar -czf $BACKUP_DIR/mailu-data-$DATE.tar.gz /opt/mailu

# Restart Mailu
docker compose up -d

# Remove backups older than 7 days
find $BACKUP_DIR -name "mailu-data-*.tar.gz" -mtime +7 -delete

Make the script executable:

1
sudo chmod +x /etc/cron.daily/backup-mailu

10.2: Set Up Certificate Auto-Renewal

Since we're using Mailu's built-in Let's Encrypt support, certificates will be automatically renewed. However, we still want to create a renewal check script:

1
sudo nano /etc/cron.weekly/check-mailu-certs

Add the following script:

1
2
3
4
#!/bin/bash
# Check Mailu container status and restart if needed
cd /opt/mailu
docker compose ps | grep -q "Exit" && docker compose restart

Make it executable:

1
sudo chmod +x /etc/cron.weekly/check-mailu-certs

10.3: Monitor Disk Space

Set up a simple disk space alert:

1
sudo nano /etc/cron.daily/check-disk-space

Add this script:

1
2
3
4
5
6
7
8
9
#!/bin/bash
THRESHOLD=90
ADMIN_EMAIL="[email protected]"

DISK_USAGE=$(df -h / | grep / | awk '{print $5}' | cut -d'%' -f1)

if [ "$DISK_USAGE" -gt "$THRESHOLD" ]; then
    echo "Warning: Disk usage is at $DISK_USAGE%" | mail -s "High Disk Usage on Mail Server" $ADMIN_EMAIL
fi

Make it executable:

1
sudo chmod +x /etc/cron.daily/check-disk-space

Final Checklist

Here's a quick summary of everything we've done to get our self-hosted email server up and running:

Server Setup

  • Selected a suitable VPS provider that allows port 25 and PTR records
  • Set up a VPS with at least 4GB RAM and 80GB storage
  • Configured PTR record for the server IP (reverse DNS)
  • Created a non-root user with sudo privileges
  • Set up SSH key authentication
  • Installed Docker and Docker Compose
  • Installed additional packages (curl, git, fail2ban, ufw)

Mail Server Configuration

  • Planned server domain structure (mail.example.com, webmail.example.com)
  • Generated Mailu configuration using setup.mailu.io
  • Downloaded configuration files (mailu.env, docker compose.yml, zonefile)
  • Created Mailu directory structure in /opt/mailu
  • Transferred and configured necessary files
  • Modified configuration settings as needed

Security Components

  • Configured firewall (UFW) with appropriate mail ports
  • Set up Fail2Ban for brute force protection (including Docker log integration and jail configurations)
  • Created custom Fail2Ban filter for web interfaces (mailu-web.conf)
  • Enabled SSL/TLS with Let's Encrypt (via Mailu setup)
  • Implemented log rotation for service logs
  • Set up automated backup system for Mailu data
  • Created certificate renewal and container status check script
  • Created disk space monitoring script

DNS Configuration

  • Added A records for mail and webmail subdomains
  • Configured MX record pointing to mail server
  • Set up SPF record to prevent email spoofing
  • Added DKIM record for email authentication
  • Configured DMARC record for reporting and policy
  • Added SRV records for client auto-discovery
  • Set CNAME records for autoconfig and autodiscover
  • Ensured all mail records are not proxied through Cloudflare

Testing & Verification

  • Verified all containers are running properly
  • Tested admin interface access
  • Created and tested a user account
  • Tested email reception from external providers
  • Tested email sending to external providers
  • Verified SPF, DKIM, and DMARC are working
  • Used external testing tools (MX Toolbox, mail-tester.com)

If you've completed all these steps, you should have a fully functional, secure multi-domain email server! If you run into any issues, double-check the corresponding section in this guide for more detailed instructions.

Summary

You should now have a fully functional, secure, and properly configured email server that supports multiple domains and users. This is now a simple and cost-effective way to handle custom domain e-mail for future projects.

In Part 3, we'll explore how to:

  • Add multiple domains to your Mailu instance
  • Set up catch-all addresses
  • Create aliases and redirections
  • Configure advanced features like spam filtering levels
  • Monitor and troubleshoot common issues
  • Set up mobile clients and desktop email applications

Further Reading / References

This post is licensed under CC BY 4.0 by the author.