Install Mailcow on VPS: Self-Hosted Email Server
Mailcow Dockerized is an all-in-one email server suite running multiple Docker containers — Postfix (SMTP), Dovecot (IMAP/POP3), Rspamd (anti-spam), ClamAV (anti-virus), Roundcube webmail, and SOGo — in a single deployment. Running Mailcow on a VPS gives you 100% control over your email without relying on third-party services.
Why Use Mailcow Instead of an External Email Service?
- Full data ownership — emails never pass through a third party
- Cost savings — no monthly G Suite/Microsoft 365 per-seat fees
- High deliverability — configure DKIM/SPF/DMARC yourself
- Webmail included — Roundcube and SOGo out of the box
- Enterprise-grade anti-spam — Rspamd + ClamAV
System Requirements
| Component | Minimum | Recommended |
|---|---|---|
| OS | Ubuntu 22.04 LTS | Ubuntu 22.04 / Debian 12 |
| RAM | 3 GB | 4–8 GB |
| CPU | 2 cores | 4 cores |
| Disk | 20 GB | 50+ GB SSD |
| Docker | 20.10+ | 24.x+ |
| Required ports | 25, 80, 110, 143, 443, 465, 587, 993, 995, 4190 | |
mail.yourdomain.com.Step 1: Prepare DNS Records
Set up DNS records before installation (allow 30 minutes to 24 hours for propagation):
# A Record — point mail subdomain to your VPS IP
mail.yourdomain.com. A YOUR.VPS.IP
# MX Record — define the mail server for your domain
yourdomain.com. MX 10 mail.yourdomain.com.
# PTR Record (Reverse DNS) — request from your hosting provider
YOUR.VPS.IP PTR mail.yourdomain.com.
# SPF Record
yourdomain.com. TXT "v=spf1 mx ~all"
# DMARC Record
_dmarc.yourdomain.com. TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]"
Step 2: Install Docker and Docker Compose
# Update the system
apt update && apt upgrade -y
# Install Docker
curl -fsSL https://get.docker.com | bash
# Verify installation
docker --version
docker compose version
Step 3: Set the System Hostname
Mailcow uses the system hostname when sending email — it must match your A record exactly.
# Set hostname (must match your A record)
hostnamectl set-hostname mail.yourdomain.com
# Verify
hostname -f
# Expected output: mail.yourdomain.com
Step 4: Clone Mailcow and Generate Configuration
# Clone from GitHub
cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized
# Auto-generate configuration
./generate_config.sh
# The script will ask for your mail server hostname
# Answer: mail.yourdomain.com
# Select timezone: Asia/Bangkok (or your timezone)
After generate_config.sh runs, a mailcow.conf file is created. Key values to review:
nano mailcow.conf
# Key settings to verify:
MAILCOW_HOSTNAME=mail.yourdomain.com
TZ=Asia/Bangkok
HTTP_PORT=80
HTTPS_PORT=443
SKIP_CLAMD=n # Enable ClamAV (n = do not skip)
SKIP_RSPAMD=n # Enable Rspamd
SKIP_SOLR=y # Disable Solr if RAM is limited
SKIP_CLAMD=y and SKIP_SOLR=y to reduce memory usage.Step 5: Start Mailcow
# Pull Docker images (may take 5–10 minutes)
docker compose pull
# Start all services
docker compose up -d
# Check status
docker compose ps
After a moment, access the web admin panel at https://mail.yourdomain.com
- Username:
admin - Password:
moohoo— change this immediately!
Step 6: Add Your Domain and First Mailbox
- Go to Web UI → Configuration → Mail Setup
- Add Domain: click Add Domain → enter
yourdomain.com - Add Mailbox: click Add Mailbox → fill in the email address
- Click Generate DKIM Keys and copy the DNS TXT record to your DNS provider
Step 7: Add the DKIM DNS Record
# The generated record looks like this:
dkim._domainkey.yourdomain.com. TXT "v=DKIM1; k=rsa; t=s; s=email; p=MIIBIjAN..."
# Verify DKIM is published
dig +short TXT dkim._domainkey.yourdomain.com
Step 8: Test Email Delivery and Score
# Send a test email via CLI
docker exec -it mailcowdockerized-postfix-mailcow-1 \
sendmail -f [email protected] [email protected] <<EOF
Subject: Test from Mailcow
Hello from my Mailcow server!
EOF
Test your email score at mail-tester.com — you should achieve 9–10/10.
Step 9: Keeping Mailcow Updated
# Update to the latest version
cd /opt/mailcow-dockerized
./update.sh
# Or update manually
git pull origin master
docker compose pull
docker compose up -d --remove-orphans
Verify Mailcow Is Running Correctly
# Check all containers
docker compose ps
# View Postfix logs
docker compose logs postfix-mailcow --tail=50
# Check Rspamd dashboard
# Visit https://mail.yourdomain.com/rspamd (via admin UI)
# Test SMTP connection
telnet mail.yourdomain.com 25
# Expected: 220 mail.yourdomain.com ESMTP Postfix
# Test IMAP connection
openssl s_client -connect mail.yourdomain.com:993
# Expected: * OK Dovecot ready
# Check mail queue
docker exec -it mailcowdockerized-postfix-mailcow-1 postqueue -p
Common Issues and Fixes
Error: "Connection refused on port 25"
Port 25 is likely blocked by your hosting provider or ISP, especially on new VPS instances. You must request that they unblock it.
# Check if port 25 is listening locally
netstat -tlnp | grep :25
# Test from an external machine
telnet YOUR.VPS.IP 25
# If Postfix is running but port 25 is unreachable externally,
# contact your provider to remove the outbound port 25 block.
Error: "550 5.7.1 Message rejected as spam"
Your email is being rejected because DKIM/SPF/DMARC is misconfigured, or your IP is blacklisted.
# Verify DKIM is published
dig +short TXT dkim._domainkey.yourdomain.com
# Verify SPF
dig +short TXT yourdomain.com | grep spf
# Check blacklists at mxtoolbox.com
# Or use Rspamd's built-in DKIM check
docker exec -it mailcowdockerized-rspamd-mailcow-1 \
rspamadm dkim_check --domain yourdomain.com
Error: "certificate verify failed" in webmail
The SSL certificate hasn't been issued yet, or it has expired. Mailcow uses Let's Encrypt and handles renewal automatically.
# Force certificate renewal
docker exec -it mailcowdockerized-acme-mailcow-1 \
/root/acme.sh --renew --domain mail.yourdomain.com --force
# Check certificate expiry
echo | openssl s_client -connect mail.yourdomain.com:443 2>/dev/null | \
openssl x509 -noout -dates
Error: "ClamAV process died" — container in restart loop
ClamAV requires at least 1.5 GB of RAM. Either add more RAM to your VPS or disable ClamAV.
# Disable ClamAV (edit mailcow.conf)
nano mailcow.conf
# Change SKIP_CLAMD=n → SKIP_CLAMD=y
docker compose up -d --remove-orphans
Error: "docker: 'compose' is not a docker command"
The Docker Compose plugin is not installed. Mailcow requires Docker 20.10+ with the compose plugin.
# Install docker compose plugin
apt install docker-compose-plugin -y
# Verify
docker compose version
Frequently Asked Questions
What VPS specs does Mailcow need to run well?
For 1–10 users, we recommend at least 4 GB RAM, 2 CPU cores, and 50 GB SSD storage. For 50+ users or heavy workloads, 8 GB RAM and 100+ GB disk is more appropriate. ClamAV alone consumes around 1–1.5 GB of RAM, so factor that in.
Does Mailcow handle SSL automatically?
Yes. Mailcow includes an acme-mailcow container that obtains and renews Let's Encrypt certificates automatically every 60 days. As long as port 80 is open and your domain's A record points to your VPS, SSL is fully hands-off.
Can I use Mailcow for transactional emails (e.g. OTP, password resets)?
Yes. Mailcow supports SMTP submission — configure your application to send via port 587 (STARTTLS) or 465 (SSL) using the credentials of any mailbox you create. It works well for low-to-medium volume transactional email. For high-volume sending, consider a dedicated relay service to avoid deliverability issues.