How to Install Modoboa Mail Server on Ubuntu

Tech reviewed: Deepak Prasad
How to Install Modoboa Mail Server on Ubuntu

Running your own mail server on Ubuntu is a common homelab and small-business goal. Modoboa wraps Postfix, Dovecot, Nginx, Amavis, SpamAssassin, and ClamAV behind a Python web admin panel and webmail—so you do not have to wire every component by hand.

This guide walks through a full Modoboa install on Ubuntu: server prep, DNS, the official modoboa-installer, post-install domain and mailbox setup, client configuration, deliverability (SPF, DKIM, DMARC, PTR), testing, and the failures people hit most often in production threads and GitHub issues. I ran the installer on Ubuntu 25.04 and kept the real terminal output below so you can compare your VPS. Where something tripped me up—missing Radicale paths, proxy env vars breaking curl, the old modo check_mx command in outdated guides—you will find the fix in the same section.

Tested on: Ubuntu 25.04 (Plucky Puffin); kernel 6.14.0-37-generic.

NOTE
Use a fresh Ubuntu VM or VPS. The installer assumes it owns Postfix, Dovecot, Nginx, and the database. Mixing it with an existing LAMP stack or another mail server usually ends badly.
WARNING
The installer also requires: 2 GB+ RAM during compile, /tmp mounted without noexec, a FQDN hostname before install, and database passwords without special characters (special chars can break the install). See the modoboa-installer README.

Quick command reference

Task Command
Set hostname sudo hostnamectl set-hostname mail.example.com
Clone installer git clone https://github.com/modoboa/modoboa-installer
Generate config sudo python3 ./run.py --stop-after-configfile-check example.com
Install sudo python3 ./run.py --interactive example.com
Debug install sudo python3 ./run.py --interactive --debug example.com
Test outbound 25 timeout 8 telnet gmail-smtp-in.l.google.com 25
Generate DKIM keys sudo /srv/modoboa/env/bin/python /srv/modoboa/instance/manage.py modo manage_dkim_keys
Repair / health check sudo /srv/modoboa/env/bin/python /srv/modoboa/instance/manage.py modo repair
Mail log sudo tail -f /var/log/mail.log

What Modoboa includes

Modoboa is a free, open-source mail hosting platform (ISC license) written in Python. A typical install bundles:

Component Role
Postfix SMTP (sending and receiving mail)
Dovecot IMAP/POP3 (mailbox access)
Nginx Web admin panel and webmail
PostgreSQL or MariaDB Modoboa configuration and mailbox metadata
Amavis + SpamAssassin + ClamAV Spam and virus filtering
OpenDKIM DKIM signing (when enabled per domain)
Let's Encrypt (certbot) TLS certificates (optional in installer.cfg)

You manage domains, mailboxes, and aliases in the browser. End users can use webmail or clients like Thunderbird and Outlook. The same installer supports Ubuntu 20.04 LTS (Focal) and newer per the official modoboa-installer.


Prerequisites

Before you run the installer, confirm you have:

Requirement Details
OS Clean Ubuntu 20.04 LTS or newer (64-bit); 22.04/24.04 LTS are common choices
RAM 2 GB minimum; 3 GB+ recommended with antivirus scanning
CPU / disk 1 vCPU is enough for light use; 20 GB+ disk for logs and mail spool
Domain A domain you control (e.g. example.com)
Hostname FQDN such as mail.example.com pointing to the server IP
DNS Working A (and MX) records before Let's Encrypt and mail tests
Port 25 Outbound SMTP open at your provider (many clouds block it by default)
Access SSH with sudo (non-root user recommended)

VPS providers and port 25

Inbound port 25 to your server is usually fine. Outbound port 25 is what providers block—and without it you cannot deliver mail directly to Gmail, Outlook, and others.

Providers often mentioned as allowing port 25 (verify current policy before you buy): OVH, Contabo, Kamatera, Hetzner, and similar bare-metal or niche VPS hosts. DigitalOcean, Vultr, and AWS commonly block outbound 25 unless you open a support ticket—and they may re-block later.

Test after install:

bash
timeout 8 bash -c 'echo quit | telnet gmail-smtp-in.l.google.com 25'

On my test host, outbound port 25 was open. You should see something like this when it works:

text
Trying 192.178.211.26...
Connected to gmail-smtp-in.l.google.com.
Escape character is '^]'.
Connection closed by foreign host.

Connection timeout or Unable to connect means you need an unblock or an SMTP relay (e.g. SendGrid, Amazon SES). See Postfix relay restrictions if you route outbound mail through a provider.


Step 1 — Plan DNS records

Mail breaks when DNS is wrong. Set these before running the installer if you use Let's Encrypt.

Required before install

Type Name Value Notes
A mail Your server IPv4 e.g. mail.example.com203.0.113.10
MX @ mail.example.com Priority 0 or 10; hostname only, not an IP
AAAA mail Your IPv6 (if used) Required if the server sends via IPv6
WARNING
If you use Cloudflare (or any CDN) for DNS, set the mail A record to DNS only (grey cloud). Proxying mail through a CDN breaks SMTP and TLS.

Set hostname on the server

bash
sudo hostnamectl set-hostname mail.example.com

Ensure /etc/hosts resolves the FQDN (add only if missing):

bash
grep -q 'mail.example.com' /etc/hosts || echo '127.0.1.1 mail.example.com mail' | sudo tee -a /etc/hosts

Verify:

bash
hostname -f

You want the FQDN you set—for example:

text
mail.example.com

After install (deliverability)

You will add SPF, DKIM, and DMARC once the domain exists in Modoboa. PTR (reverse DNS) is set at your hosting provider, not your domain registrar—value should be mail.example.com.


Step 2 — Update Ubuntu and install dependencies

Log in via SSH and update packages:

bash
sudo apt update
sudo apt upgrade -y

Install tools the installer needs:

bash
sudo apt install -y git python3-virtualenv python3-pip curl gnupg2 dnsutils inetutils-telnet

See apt on Linux if you are new to package management on Ubuntu. curl is handy later when you probe the web UI from the shell.

Optional but useful if the installer fails on Cairo-related Python packages:

bash
sudo apt install -y libcairo2-dev pkg-config python3-dev

Before the first install, create the Radicale config directory. This one caught me on a retest—the installer can fail if /etc/modoboa-radicale does not exist yet:

bash
sudo mkdir -p /etc/modoboa-radicale

After dependencies install, git --version on my host reported:

text
git version 2.48.1
TIP
Create a dedicated sudo user instead of using root for daily work: adduser deploy && sudo usermod -aG sudo deploy, then su - deploy.

Step 3 — Download and configure the Modoboa installer

Clone the official installer:

bash
git clone https://github.com/modoboa/modoboa-installer
cd modoboa-installer
ls -l run.py

Clone and listing looked like this on my machine:

text
Cloning into 'modoboa-installer'...
-rwxr-xr-x 1 user user 10336 ... run.py

Generate installer.cfg (replace example.com with your apex domain, not mail.example.com):

bash
sudo python3 ./run.py --stop-after-configfile-check example.com

The config check prints a short welcome and creates installer.cfg when it is missing:

text
Welcome to Modoboa installer!

Checking the installer...
Installer seems up to date!
Checks complete

Configuration file installer.cfg not found, creating new one.

Edit the config:

bash
sudo nano installer.cfg

Fresh installer.cfg files usually contain lines like these (sudo grep -E '^(hostname|type|engine)' installer.cfg):

text
hostname = mail.%(domain)s
type = amavis
type = self-signed
engine = postgres

Common installer.cfg settings

TLS — production (recommended):

ini
[certificate]
type = letsencrypt

[letsencrypt]
email = [email protected]

TLS — lab / internal only:

ini
[certificate]
type = self-signed

Database — PostgreSQL (default):

ini
[database]
engine = postgres
host = 127.0.0.1
install = true

Database — MariaDB/MySQL:

ini
[database]
engine = mysql
host = 127.0.0.1
install = true

Use a real email for Let's Encrypt; the default placeholder address will cause certificate issuance to fail. For more on TLS in front of Nginx, see install an SSL certificate on Nginx.


Step 4 — Run the installer

Confirm DNS for mail.example.com resolves to this server:

bash
dig +short mail.example.com A

The command should print your server IP. Empty output means the A record is missing or not propagated yet.

Start the interactive install:

bash
sudo python3 ./run.py --interactive example.com

Confirm when prompted. On my test host (3 vCPU, 8 GB RAM), the full run.py pass took about 10 minutes end to end. Budget 15–25 minutes on a fresh 2 GB VPS—ClamAV definition downloads and pip compiles are usually the slow part.

Near the start of a successful run you should see the component list and install banners:

text
Your mail server will be installed with the following components:
fail2ban modoboa amavis clamav dovecot nginx postfix postwhite spamassassin uwsgi radicale opendkim
The process can be long, feel free to take a coffee and come back later ;)
Starting...
Generating new self-signed certificate
Installing fail2ban
Installing modoboa
Installing radicale
Installing uwsgi
Installing nginx
Installing postfix
Installing dovecot
Installing amavis
Installing spamassassin
Installing clamav
Installing opendkim

When it finishes cleanly, the installer prints the admin URL and default password:

text
Congratulations! You can enjoy Modoboa at https://mail.example.com (admin:password)

Verify services and web UI

After install, confirm core services and ports:

bash
systemctl is-active nginx postfix dovecot uwsgi postgresql
ss -lnpt | grep -E ':443|:25|:587|:993'
curl --noproxy '*' -k -sI --resolve mail.example.com:443:127.0.0.1 https://mail.example.com/accounts/login/

Use --noproxy '*' if your shell has http_proxy/https_proxy set—otherwise curl may fail against localhost. That bit me on a host with proxy env vars exported.

Core services on my install were all active:

text
nginx              active
postfix            active
dovecot            active
uwsgi              active
postgresql         active

Listening mail and web ports looked like this:

text
LISTEN 0  100  0.0.0.0:25    0.0.0.0:*
LISTEN 0  511  0.0.0.0:443   0.0.0.0:*
LISTEN 0  100  0.0.0.0:587   0.0.0.0:*
LISTEN 0  100  0.0.0.0:993   0.0.0.0:*
LISTEN 0  100  127.0.0.1:9999  0.0.0.0:*

The login page returned HTTP/2 200 with a Modoboa title:

text
HTTP/2 200
server: nginx/1.26.3 (Ubuntu)
content-type: text/html; charset=utf-8

Page title: Welcome to Modoboa. Modoboa version installed: 2.9.0. I did not run a full Gmail round-trip on the test domain—that needs real DNS and mailboxes in the admin UI (covered in Step 5).

If it fails, retry with debug output:

bash
sudo python3 ./run.py --interactive --debug example.com

Reboot once to ensure all services start cleanly:

bash
sudo shutdown -r now

Default login

Field Value
URL https://mail.example.com
Username admin
Password password

Change the password immediately: Admin → Settings → Profile.


Step 5 — Add your domain and mailboxes

Add the mail domain

  1. Open the admin panel → DomainsAdd.
  2. Enter example.com as the domain name.
  3. Enable DKIM signing (selector e.g. modoboa, key length 2048).
  4. Optionally create a domain admin; SMTP best practice expects a [email protected] address.

Create a mailbox

  1. Domains → select your domain → MailboxesAddAccount.
  2. Role: Simple user.
  3. Set username (e.g. alice) and password.

Create an alias (optional)

Domains → your domain → AliasesAdd — e.g. [email protected][email protected].


Step 6 — Publish SPF, DKIM, and DMARC

These records live at your DNS host. They are what Gmail, Microsoft, and Yahoo check before accepting your mail.

SPF (TXT on apex domain)

Name Value
@ v=spf1 mx ~all

Tighten later with ip4:YOUR.SERVER.IP if you send only from this host.

Verify:

bash
dig example.com TXT +short

Until you publish your own record, dig against a reserved name like example.com may show the IANA placeholder:

text
"v=spf1 -all"

Your domain should show your own SPF record after you publish it.

DKIM (TXT record)

In Modoboa: Domains → your domain → DNSShow key. Copy the Bind/named format.

Name Value
modoboa._domainkey Paste the key content (no extra quotes)

If DKIM keys do not appear after several hours, generate them manually:

bash
sudo /srv/modoboa/env/bin/python /srv/modoboa/instance/manage.py modo manage_dkim_keys

You may see harmless Django model warnings—keys still generate:

text
amavis.Msgrcpt.mail: (fields.W342) Setting unique=True on a ForeignKey ...
amavis.Quarantine.mail: (fields.W342) Setting unique=True on a ForeignKey ...

DMARC (TXT record)

Name Value
_dmarc v=DMARC1; p=none; pct=100; rua=mailto:[email protected]

Start with p=none to collect reports; move to quarantine or reject when confident.

PTR (reverse DNS)

At your VPS panel, set reverse DNS for your IPv4 (and IPv6 if used) to mail.example.com.

Check:

bash
dig -x YOUR.SERVER.IP +short

When PTR is set correctly at your host, you should get your mail hostname:

text
mail.example.com.

Run Modoboa repair checks

After DNS propagates and you add domains in the admin UI, run:

bash
sudo /srv/modoboa/env/bin/python /srv/modoboa/instance/manage.py modo repair

Typical output flags objects that need an owner or alias—run this after DNS propagates and you add domains in the admin UI:

text
Checking for... Sometime objects have no owner....
Checking for... Sometime mailboxes have no alias....

Older Modoboa guides mention modo check_mx; that subcommand is not available in Modoboa 2.9.x—use modo repair and dig instead.


Step 7 — Test sending and receiving

Webmail

Log out of the admin account. Log in as your mailbox user at the same URL. Compose a message to an external address (Gmail, etc.) and reply from outside to your new mailbox.

mail-tester.com

  1. Visit mail-tester.com.
  2. Send an email from your Modoboa account to the address shown.
  3. Review score and fix SPF, DKIM, PTR, or content issues reported.

Aim for 8/10 or higher before relying on the server for production mail.


Step 8 — Configure email clients (Thunderbird / Outlook)

Modoboa supports AutoMX for automatic client discovery; manual settings work everywhere.

Setting Incoming (IMAP) Outgoing (SMTP)
Server mail.example.com mail.example.com
Port 993 (SSL/TLS) or 143 (STARTTLS) 587 (STARTTLS)
Auth Normal password Normal password
Username Full email address Full email address

Microsoft Outlook may require SMTPS on port 465—enable it in Postfix if clients cannot send.

Accept the Let's Encrypt certificate in the client; with self-signed certs you must trust the certificate manually.


Troubleshooting common Modoboa issues on Ubuntu

Greylisting delays inbound mail

Modoboa enables Postfix greylisting by default. Mail may arrive after a few minutes. Log hint:

text
postfix/postscreen: NOQUEUE: reject: ... 450 4.3.2 Service currently unavailable

To disable, comment out postscreen_* lines at the end of /etc/postfix/main.cf, then:

bash
sudo systemctl restart postfix

Policy daemon not listening (port 9999)

If /var/log/mail.log shows:

text
warning: connect to 127.0.0.1:9999: Connection refused

Restart supervisor:

bash
sudo systemctl restart supervisor
sudo ss -lnpt | grep 9999

When policyd is running, port 9999 should be listening locally:

text
LISTEN 0 100 127.0.0.1:9999 0.0.0.0:*

If still down, comment out check_policy_service inet:127.0.0.1:9999 in smtpd_recipient_restrictions inside /etc/postfix/main.cf and restart Postfix.

Web interface 502 or connection refused

bash
sudo systemctl status nginx --no-pager
sudo nginx -t
sudo systemctl restart nginx

A healthy config test looks like:

text
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Check logs: /var/log/nginx/error.log and /var/log/mail.log.

Out of memory (ClamAV / database killed)

On 1 GB RAM, clamav-freshclam or ClamAV may loop or OOM-kill other services. Options:

  • Upgrade to 2–3 GB RAM.
  • Temporarily stop freshclam during install: sudo systemctl stop clamav-freshclam.
  • Tune ClamAV memory limits for small VPS workloads.

Minimum RAM notes from community installs: ~1 GB without ClamAV, ~2 GB with ClamAV.

Installation fails (radicale / partial install)

Some installs failed when /etc/modoboa-radicale was missing. If the installer errors on calendar (Radicale) setup:

bash
sudo mkdir -p /etc/modoboa-radicale

After creating it, ls should show the directory:

text
drwxr-xr-x 2 root root 4096 ... /etc/modoboa-radicale

Re-run with --debug. If a previous attempt left state behind, use a fresh VM or purge leftover packages (postfix, dovecot, nginx, postgresql, clamav, etc.) before retrying—the installer has no official uninstall command.

Let's Encrypt rate limits or wrong hostname

Ensure mail.example.com resolves publicly before install. After five failed attempts, Let's Encrypt may rate-limit—fix DNS, wait, and re-run certbot or the installer.

Renew TLS certificates

Modoboa installs certbot (often under /opt/certbot-auto). Add a daily cron as root:

bash
sudo crontab -e
cron
@daily /opt/certbot-auto renew -q && systemctl reload nginx postfix dovecot

Adjust the certbot path if yours differs: sudo find / -name '*certbot*' 2>/dev/null.

Port 25 blocked — use an SMTP relay

Configure Postfix to relay through a provider such as SendGrid when outbound 25 is blocked. See SendGrid Postfix integration for relay syntax and Postfix relay restrictions for policy context on this site.


Optional: reverse proxy and TLS offload

Homelab setups sometimes put HAProxy, Caddy, or Nginx in front of Modoboa on a single public IP. That is advanced: you must forward TCP 25, 587, 993, and 443 (or terminate TLS consistently) without breaking SMTP STARTTLS. Document your proxy config separately; the stock installer expects Nginx on the mail host.


Security and maintenance habits

  • Change default admin / password immediately.
  • Keep Ubuntu updated: sudo apt update && sudo apt upgrade.
  • Monitor blacklists and DMARC reports Modoboa can surface.
  • Back up /srv/modoboa, Postfix/Dovecot configs, and your database regularly.
  • Do not expose unused admin ports; use firewall rules allowing only 22, 25, 80, 443, 587, 993 as needed.

For timezone and system clock accuracy (important for TLS and mail timestamps), see set timezone on Ubuntu.


Summary

Modoboa on Ubuntu is one of the faster paths to a self-hosted mail stack: the installer handles Postfix, Dovecot, Nginx, filtering, and the web UI on Ubuntu 20.04 LTS and newer. On my test host the full install took about 10 minutes; plan 15–25 minutes on a fresh 2 GB VPS where ClamAV and pip have to download from scratch.

Success still depends on the boring parts—DNS (MX, A, PTR, SPF, DKIM, DMARC), a clean server with enough RAM, and outbound port 25 or a relay. After install, confirm the login page returns HTTP 200, change the default admin password, add domains and mailboxes in the admin UI, then test with mail-tester.com before you rely on the server for production mail. If outbound 25 is blocked, relay through SendGrid or SES rather than fighting your VPS provider.

Official references: Modoboa documentation and modoboa-installer on GitHub.


Frequently Asked Questions

1. What are the minimum requirements to install Modoboa on Ubuntu?

Use a fresh Ubuntu server (20.04 LTS or newer per the official installer) with at least 2 GB RAM (3 GB is safer if ClamAV and SpamAssassin run). You need a registered domain, sudo access, correct DNS records, and a VPS provider that allows outbound SMTP on port 25 if you want to send mail directly.

2. What is the default Modoboa admin login after installation?

The installer creates admin with password password. Log in at https://mail.your-domain.com (or your configured hostname), then change the password immediately under Admin → Settings → Profile.

3. Which database does the Modoboa installer use?

PostgreSQL is the default (engine = postgres in installer.cfg). You can switch to MariaDB/MySQL by setting engine = mysql before running the installer.

4. Why can I receive email but not send it from Modoboa?

The most common cause is outbound port 25 blocked by your VPS provider. Test with telnet gmail-smtp-in.l.google.com 25. If blocked, request an unblock or configure an SMTP relay (for example SendGrid) in Postfix.

5. How do I improve email deliverability with Modoboa?

Set PTR (reverse DNS) to mail.your-domain.com, add SPF and DKIM TXT records, publish a DMARC record, enable DKIM signing when adding the domain in Modoboa, and test with mail-tester.com.

6. Can I install Modoboa on an existing Ubuntu server with other services?

The official installer expects a clean system. Installing on a server that already runs a web server, database, or mail stack often causes conflicts. Use a dedicated VM.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, …

  • Red Hat Certified System Administrator in Red Hat OpenStack
  • Certified Kubernetes Application Developer (CKAD)
  • Red Hat Certified Specialist in Ansible Automation
  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security