How to Install Mailu on Ubuntu 24.04 VPS: Docker-Based Mail Server Suite
Running your own mail server used to mean hand-configuring Postfix, Dovecot, SpamAssassin, OpenDKIM, and a webmail client — each with its own config dialect and each a potential foot-gun. Mailu collapses the entire stack into a well-tested set of Docker images with a single environment file and a clean admin UI. This guide walks you through deploying Mailu on an Ubuntu 24.04 VPS from scratch, including DNS setup, TLS, DKIM, deliverability testing, backups, and common troubleshooting scenarios.
Want a managed alternative? If you prefer not to run your own mail server, our team can host a fully managed Mailu instance on dedicated infrastructure. Contact us to discuss managed email hosting. Otherwise, read on — with a decent VPS you can be sending and receiving mail in under an hour.
Table of Contents
What is Mailu?
Mailu is a simple yet complete, open-source mail server packaged as a Docker Compose stack. Instead of gluing together half a dozen packages by hand, you deploy a curated set of containers that are tested together and upgraded as a unit. The stack includes Postfix (SMTP), Dovecot (IMAP/POP3), Rspamd (spam filtering and DKIM signing), Unbound (recursive DNS resolver), an admin UI for domain and user management, and your choice of webmail (Roundcube, SnappyMail, or none). Optional add-ons include ClamAV antivirus, Fetchmail for pulling mail from external accounts, and a full Let's Encrypt integration for automatic TLS renewal.
Mailu has been in active development since 2015 and is the preferred self-hosted mail stack for thousands of small businesses, homelab enthusiasts, and privacy-conscious teams. It is designed around sensible defaults: sane spam thresholds, modern TLS ciphers, DMARC-aware policies, and an opinionated UI that hides complexity until you need it. For admins who want to extend the setup, every component can be tuned through mailu.env and per-service overrides.
Common use cases include family or team email on a custom domain, transactional email for applications (signup confirmations, password resets), distribution lists and aliases for small organizations, catch-all mailboxes for ecommerce stores, and private backup MX for a primary provider. Mailu also scales up to a clustered HA setup when you need redundancy, though most single-domain deployments run comfortably on one modest VPS.
Why Run Your Own Mail Server?
Managed providers like Google Workspace and Microsoft 365 are convenient, but self-hosting mail with Mailu has concrete advantages:
- Data sovereignty -- Every message, attachment, and address book lives on hardware you control. No third party can scan your mail, train models on it, or hand it over without your knowledge.
- Unlimited mailboxes and aliases -- Pay a flat VPS fee instead of per-seat licensing. Aliases, catch-alls, and shared mailboxes cost nothing extra.
- Full control over filtering -- Tune Rspamd scores, whitelist partners, blacklist abusers, and write custom Sieve rules without waiting on a support ticket.
- GDPR and compliance -- For EU businesses, keeping mail inside the EU on a known jurisdiction simplifies Article 28 processor agreements.
- Transactional email at scale -- Send password resets, invoices, and notifications from your own IP without per-message fees.
- A backup MX -- Run Mailu as a secondary mail exchanger that queues mail during primary outages.
Cost Comparison: Mailu vs. Managed Email
| Scenario | Google Workspace Business | Microsoft 365 Business Basic | Self-Hosted Mailu (VPS) |
|---|---|---|---|
| Monthly cost (10 users) | ~$120/mo | ~$60/mo | EUR 19.99/mo (unlimited users) |
| Mailbox storage | 30 GB/user | 50 GB/user | Limited only by VPS disk |
| Custom domains | 1 | 1 | Unlimited |
| Aliases per user | 30 | 400 | Unlimited |
| Data leaves your server? | Yes | Yes | No |
| Catch-all addresses | No | Limited | Yes |
Mailu vs. Mailcow vs. Mail-in-a-Box
If you have shopped around, you have probably run into the three big self-hosted options. A quick side-by-side:
| Feature | Mailu | Mailcow | Mail-in-a-Box |
|---|---|---|---|
| Deployment | Docker Compose | Docker Compose | Bare-metal install script |
| Resource footprint | Lower (~2 GB RAM) | Higher (~6 GB RAM) | Medium (~2 GB RAM) |
| Webmail options | Roundcube, SnappyMail | SOGo | Roundcube |
| Calendars/contacts | Radicale (optional) | SOGo (built-in) | Nextcloud (bundled) |
| Admin UI polish | Clean, minimal | Feature-rich | Basic |
| HA / clustering | Documented | Third-party | Not supported |
| Best for | Small-to-medium setups, low RAM VPS | Teams needing groupware | Bare-metal single-domain installs |
docker compose pull.Prerequisites
Before you start, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- At least 2 GB of RAM and 20 GB of disk (4 GB RAM recommended if you enable ClamAV)
- A registered domain you control DNS for (we use
example.comin this guide) - Port 25 outbound reachable — many low-tier VPS providers block it by default
- SSH access to the server
Recommended Plan: CloudCore Professional>
Mail workloads are IO-heavy and ClamAV chews RAM. For a comfortable Mailu install we recommend the CloudCore Professional plan:>
- 6 vCPU cores
- 12 GB RAM
- 100 GB NVMe SSD
- Port 25 unblocked on request
- EUR 19.99/month>
This leaves headroom for ClamAV, Rspamd training data, and years of mailbox growth.
Connect to your server:
ssh root@your-server-ipStep 1: Verify Port 25 Is Reachable
Running a mail server is pointless if outbound port 25 is blocked. Most consumer ISPs and many cloud providers (AWS, GCP, Azure, Oracle) block it by default. Check before you go any further.
Install a small netcat helper and test a connection to a public SMTP server:
sudo apt install -y netcat-openbsd
nc -vz gmail-smtp-in.l.google.com 25Expected output:
Connection to gmail-smtp-in.l.google.com (142.251.12.27) 25 port [tcp/smtp] succeeded!If the command hangs or returns Connection timed out, your provider is filtering port 25. Open a support ticket to request it be unblocked, or switch to a provider that permits outbound SMTP. On vps-server.host, port 25 is unblocked by default for verified accounts.
Step 2: Configure DNS Records
Mail deliverability hinges on correct DNS. Configure the following records in your domain's DNS control panel before you deploy Mailu — most providers propagate within minutes, but TTLs can stretch it to hours.
Replace example.com with your domain and 203.0.113.42 with your VPS IPv4 address.
| Type | Host | Value | Notes |
|---|---|---|---|
| A | mail.example.com | 203.0.113.42 | Server hostname |
| AAAA | mail.example.com | 2001:db8::42 | Only if you have IPv6 |
| MX | example.com | 10 mail.example.com. | Routes inbound mail |
| A | example.com | 203.0.113.42 | For webmail at apex |
| TXT | example.com | v=spf1 mx ~all | SPF policy |
| TXT | _dmarc.example.com | v=DMARC1; p=quarantine; rua=mailto:[email protected] | DMARC policy |
| TXT | autoconfig.example.com | v=spf1 -all | Optional client autoconfig |
Verify records propagated:
dig +short MX example.com
dig +short A mail.example.com
dig +short TXT example.comExpected output:
10 mail.example.com.
203.0.113.42
"v=spf1 mx ~all"Step 3: Set Reverse DNS (PTR)
Reverse DNS is the single most important factor for inbox placement with Gmail, Outlook, and Yahoo. Your VPS IP must resolve back to mail.example.com.
On vps-server.host, set reverse DNS in the client area under "Networking → rDNS". On other providers, look for "PTR record" or "reverse DNS" in the instance management panel. The value you set is the hostname, not an IP:
mail.example.comVerify:
dig +short -x 203.0.113.42Expected output:
mail.example.com.If this returns nothing or the wrong hostname, fix it before continuing. Mail servers without matching PTR records are almost universally rejected or quarantined.
Step 4: Update System and Install Docker
Mailu ships as Docker containers, so you need Docker Engine and the Compose plugin.
Update packages:
sudo apt update && sudo apt upgrade -yInstall Docker using the official convenience script. For a hand-rolled install with apt repositories, see our detailed guides on how to install Docker on Ubuntu and how to install Docker Compose on Ubuntu.
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USERLog out and back in so the group membership takes effect. Verify:
docker --version
docker compose versionExpected output:
Docker version 27.3.1, build ce12230
Docker Compose version v2.29.7Enable Docker to start at boot:
sudo systemctl enable --now dockerWhile you are setting up, install fail2ban to rate-limit brute-force attempts against SMTP and IMAP auth:
sudo apt install -y fail2banStep 5: Generate Mailu Configuration with the Setup Wizard
Mailu does not ship a single docker-compose.yml — instead, the project runs a web-based setup wizard at setup.mailu.io that generates a mailu.env and docker-compose.yml tailored to your choices. This avoids half the "which flag did I forget" problems of other mail stacks.
Open https://setup.mailu.io in your browser and pick the current stable version (for example, 2024.06). Work through the prompts:
Page 1 — General
- Main mail domain:
example.com - Hostnames:
mail.example.com - Public hostnames:
mail.example.com - Webmail:
Roundcube(orSnappyMailif you prefer a lighter UI) - Admin UI:
enabled - API: optional; enable if you plan to automate user creation
- IPv4 listen address: leave blank (listens on all interfaces)
- IPv6 listen address:
::if you have IPv6 - Enable IPv6: yes, if your VPS has an IPv6 address
- Subnet:
192.168.203.0/24(the default, isolated Docker network) - Public hostnames: comma-separate any extra domains you will host
- TLS certificates:
letsencrypt— Mailu will auto-obtain and renew certs from Let's Encrypt - Fetchmail:
enabledonly if you want Mailu to pull mail from external IMAP/POP3 accounts - Antivirus:
ClamAVif you have 4 GB+ RAM, otherwisenone
- Enable WebDAV (Radicale) for calendar/contacts: optional
- Admin URL:
/admin - Webmail URL:
/webmail - Relayed domains: blank
- Relay hosts: blank unless you smart-host through another provider
Step 6: Deploy the Mailu Stack
Create the Mailu directory and pull the generated files. Replace the URLs below with the ones the wizard gave you:
sudo mkdir -p /mailu
cd /mailu
sudo wget https://setup.mailu.io/2024.06/file/YOUR_TOKEN/mailu.env
sudo wget https://setup.mailu.io/2024.06/file/YOUR_TOKEN/docker-compose.ymlInspect the env file — it contains your secrets, admin password seed, and all feature flags:
sudo less /mailu/mailu.envAdjust anything the wizard missed. Two values to double-check:
DOMAIN=example.com
HOSTNAMES=mail.example.com
POSTMASTER=admin
TLS_FLAVOR=letsencryptOpen the mail-related firewall ports. If you use UFW:
sudo ufw allow 25/tcp # SMTP (inbound)
sudo ufw allow 80/tcp # HTTP for Let's Encrypt challenge
sudo ufw allow 443/tcp # HTTPS for webmail + admin
sudo ufw allow 465/tcp # SMTPS (submission)
sudo ufw allow 587/tcp # Submission
sudo ufw allow 993/tcp # IMAPS
sudo ufw allow 995/tcp # POP3S
sudo ufw reloadPull the images and bring the stack up in detached mode:
cd /mailu
sudo docker compose pull
sudo docker compose up -dExpected output (abbreviated):
[+] Running 12/12
✔ Network mailu_default Created
✔ Container mailu-redis-1 Started
✔ Container mailu-resolver-1 Started
✔ Container mailu-front-1 Started
✔ Container mailu-admin-1 Started
✔ Container mailu-imap-1 Started
✔ Container mailu-smtp-1 Started
✔ Container mailu-antispam-1 Started
✔ Container mailu-webmail-1 Started
...Check that all containers are healthy:
sudo docker compose psYou should see every container in running or healthy state. If any are restarting, jump to Troubleshooting.
Step 7: Create the Admin Account
The admin UI is empty until you bootstrap the first superuser. Mailu ships a Flask CLI inside the admin container for exactly this.
sudo docker compose exec admin flask mailu admin root example.com StrongP@ssw0rdThe three positional arguments are username, domain, and password. This creates [email protected] with full admin rights.
Expected output:
[email protected] createdOpen your browser and navigate to:
https://mail.example.com/adminLet's Encrypt may take 30-60 seconds the first time to issue the cert — if you see a TLS warning immediately, wait a minute and refresh. Log in with [email protected] and the password you just set.
Step 8: Add Your Domain and Retrieve the DKIM Key
Inside the admin UI:
example.com (created automatically from mailu.env)v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEF...Add it to your DNS as a TXT record:
| Type | Host | Value |
|---|---|---|
| TXT | dkim._domainkey.example.com | v=DKIM1; k=rsa; p=MIIBIjANBgkq... |
dig +short TXT dkim._domainkey.example.comBack in the admin UI, create your first real mailbox under Users → New user — for example [email protected]. This is the account you will use for day-to-day mail and for deliverability testing in the next step.
Step 9: Test Deliverability
The single best deliverability check is mail-tester.com. It gives you a throwaway email address, you send to it, and it grades your setup out of 10.
[email protected])https://mail.example.com/webmailA healthy Mailu install with correct DNS scores 9/10 or 10/10. Common deductions and fixes:
- SPF -0.5: record missing
mxmechanism. Update SPF tov=spf1 mx ~all - DKIM fail: key mismatch. Regenerate in admin UI and re-add TXT record
- DMARC missing: add the
_dmarcTXT record from Step 2 - PTR missing: set reverse DNS in your VPS control panel (Step 3)
- Listed on blocklist: the VPS IP was previously abused. Request a new IP or file a delisting request at mxtoolbox.com/blacklists.aspx
SPF: PASS
DKIM: PASS
DMARC: PASSThree passes means you are landing in the inbox.
Step 10: Configure Backups
Mailu stores everything under /mailu — mailboxes, Rspamd training data, Let's Encrypt certs, and the admin SQLite database. Back up the directory and you back up the whole server.
A simple nightly backup with restic to an S3-compatible bucket:
sudo apt install -y restic
export RESTIC_REPOSITORY="s3:s3.example.com/mailu-backups"
export RESTIC_PASSWORD="use-a-strong-passphrase"
restic initCreate a backup script at /usr/local/bin/mailu-backup.sh:
#!/bin/bash
set -euo pipefail
cd /mailu
docker compose stop
restic backup /mailu --tag mailu-daily
docker compose start
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prunesudo chmod +x /usr/local/bin/mailu-backup.shSchedule it with a systemd timer or cron entry at 03:00 daily:
sudo crontab -eAdd:
0 3 * /usr/local/bin/mailu-backup.sh >> /var/log/mailu-backup.log 2>&1For a hot backup that does not stop the stack, snapshot the underlying filesystem (ZFS, Btrfs, LVM) instead of stopping containers — mailbox writes during backup can otherwise corrupt the snapshot.
Step 11: Keep Mailu Updated
Mailu releases patch versions throughout each quarter and minor versions twice a year. Updates are one-liners.
Before upgrading a major version (for example 2024.06 to 2025.06), read the release notes and bump the version string in both mailu.env and docker-compose.yml.
Patch upgrades within the same major version:
cd /mailu
sudo docker compose pull
sudo docker compose up -dExpected output:
[+] Pulling 12/12
✔ front Pulled
✔ admin Pulled
...
[+] Running 12/12
✔ Container mailu-front-1 Recreated
...Clean up old images to reclaim disk space:
sudo docker image prune -fCheck the changelog after every upgrade by tailing the admin container log:
sudo docker compose logs -f adminClient Configuration
Give your users these settings for desktop clients (Thunderbird, Outlook, Apple Mail) and mobile (iOS, K-9):
| Setting | Value |
|---|---|
| IMAP server | mail.example.com |
| IMAP port | 993 |
| IMAP security | SSL/TLS |
| SMTP server | mail.example.com |
| SMTP port | 465 (implicit TLS) or 587 (STARTTLS) |
| SMTP security | SSL/TLS or STARTTLS |
| Username | Full email address, e.g. [email protected] |
| Password | User's Mailu password |
| Authentication | Normal password |
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| Outbound port 25 times out | Provider blocks egress on port 25 | Confirm with nc -vz gmail-smtp-in.l.google.com 25. Open a support ticket to unblock or migrate to a SMTP-friendly host. |
front container restarts with TLS errors | Let's Encrypt rate-limited or HTTP-01 challenge failing | Check logs: docker compose logs front. Ensure ports 80/443 are open and mail.example.com resolves to the VPS IP. Wait 1 hour if rate-limited. |
| Mail lands in Gmail Spam | Missing or wrong SPF/DKIM/DMARC/PTR | Run mail-tester, fix the flagged records, wait for TTL, resend. |
| DKIM lookup fails | DNS TXT not propagated or split across two strings | Verify: dig +short TXT dkim._domainkey.example.com. Single-string records over 255 chars must be split per DNS spec — most providers handle this automatically. |
| Admin UI shows "502 Bad Gateway" | Admin container still starting or unhealthy | Wait 60 seconds. Check: docker compose ps and docker compose logs admin. Restart with docker compose restart admin. |
| Bounces with "relay access denied" | Client trying to send without authenticating | Ensure client is using port 465/587 with authentication, not port 25. |
| ClamAV uses 2 GB+ RAM | Signature database loaded in memory | Expected. Disable ClamAV in mailu.env (ANTIVIRUS=none) and recreate the stack if RAM-constrained. |
| Mail quarantined in Rspamd | Score above threshold | Log into admin UI → Antispam → review quarantine. Whitelist senders under Settings → Relayed domains / Override. |
| TLS cert expired | front container could not renew | Check docker compose logs front</td><td>grep -i acme<code>. Common cause: port 80 blocked mid-renewal. Reopen port 80 and force renewal with </code>docker compose restart front. |
Permission denied on /mailu volumes | Host UID mismatch after a host migration | sudo chown -R 1000:1000 /mailu then docker compose restart. |
Viewing Logs
Tail live logs from every service:
cd /mailu
sudo docker compose logs -fFocus on a single service:
sudo docker compose logs -f admin
sudo docker compose logs -f smtp
sudo docker compose logs -f antispamSee the last 100 lines across the stack:
sudo docker compose logs --tail=100FAQ
How much RAM does Mailu really need?
A bare Mailu install (Postfix, Dovecot, Rspamd, admin, Roundcube, no ClamAV) runs comfortably in 2 GB of RAM under normal load for a small team — perhaps 5-20 mailboxes. Adding ClamAV pushes the working set to around 3-4 GB because the signature database loads into memory on startup. If you expect heavy inbound volume or many concurrent IMAP clients, plan for 6-8 GB. Our CloudCore Professional plan at 12 GB is a generous fit that also accommodates future growth like a second domain or a WebDAV calendar server.
Can I host multiple domains on one Mailu install?
Yes. In the admin UI, go to Mail domains → New domain and add as many domains as you like. Each gets its own DKIM key, its own users, and its own aliases. Point their MX records at mail.example.com (the same primary hostname) and Mailu routes inbound mail based on the recipient domain. For sending, each domain's outbound mail is signed with its own DKIM key automatically.
How do I migrate existing mailboxes from another provider?
Mailu supports two migration paths. For a one-time bulk move, use imapsync on a separate machine to copy every message from the source IMAP server into Mailu — this preserves folders, flags, and timestamps. For an ongoing pull (useful during a transition period), enable Fetchmail in mailu.env and add accounts in the admin UI under Mail domains → Fetched accounts. Fetchmail polls the remote server every few minutes and copies new mail into the Mailu mailbox.
Is Mailu safe to expose directly to the internet?
Yes — that is its intended deployment. The front container (Nginx) is the only service with internet-facing ports, and it proxies to internal containers on a private Docker network (192.168.203.0/24 by default). All inter-container traffic stays on that subnet. Still, harden the host with fail2ban to throttle brute-force SMTP and IMAP auth attempts, keep SSH on a non-default port with key-only auth, and enable automatic security updates. Rotate your admin password regularly and never expose the admin UI over plain HTTP.
What happens if my VPS IP ends up on a blocklist?
IP reputation is the hardest part of running your own mail. Check all major blocklists at mxtoolbox.com/blacklists.aspx. If you find yourself listed, each provider has a delisting form — most (Spamhaus, Barracuda, SORBS) delist within 24-48 hours once you explain the cause and confirm it is fixed. For fresh VPS IPs that were previously abused, the fastest fix is often to request a new IP from your provider. On vps-server.host we rotate IPs on request for verified accounts.
Can Mailu run behind Cloudflare?
The webmail and admin UI can sit behind Cloudflare proxied records (orange cloud). The MX and mail.example.com A record must be unproxied (grey cloud) because Cloudflare does not proxy SMTP traffic — they need to resolve directly to your VPS IP. Mixing proxied webmail with unproxied mail endpoints on separate hostnames works well in practice.
Next Steps
Now that Mailu is live, here are good follow-ups:
- Set up monitoring -- Deploy Uptime Kuma and configure SMTP + IMAP probes against
mail.example.com. Get paged when delivery breaks instead of discovering it from angry users.
- Install fail2ban for brute-force protection -- Our fail2ban guide includes Mailu-specific jails for Postfix and Dovecot that ban IPs after a handful of failed auth attempts.
- Harden with a reverse proxy -- If you run other services on the same VPS, front everything with Caddy or Traefik for unified TLS and routing.
- Automate with the Mailu API -- Enable the admin API and create mailboxes programmatically from your app's signup flow. Every admin UI action has an API equivalent documented in the Mailu admin reference.
- Add calendar and contacts -- Enable the optional Radicale container to serve CalDAV/CardDAV from the same hostname. iOS, macOS, and Thunderbird all speak it natively.
- Review the official docs -- The Mailu docs at mailu.io cover advanced topics like clustered HA, Kubernetes deployments, and custom Rspamd rules.
Skip the Manual Install — Get Mail Hosting That Just Works>
Our CloudCore Professional VPS gives Mailu everything it needs to hit 10/10 on mail-tester out of the box: unblocked port 25, clean IP reputation, reverse DNS on request, and NVMe storage for fast IMAP.>
- 6 vCPU, 12 GB RAM, 100 GB NVMe SSD
- Port 25 unblocked for verified accounts
- Free reverse DNS configuration
- IPv4 + IPv6 included
- 99.99% uptime SLA>
Deploy a Mail-Ready VPS Now — EUR 19.99/month, unlimited mailboxes.