Skip to main content

How to Self-Host Mailcow: Complete Email Server 2026

·OSSAlt Team
mailcowemail-serverself-hostingdockersmtp2026

TL;DR

Mailcow (GPL 2.0, ~8K GitHub stars) is the most complete self-hosted email server stack. It ships Postfix (SMTP), Dovecot (IMAP), SOGo (webmail + CalDAV/CardDAV), Rspamd (spam filtering), ClamAV (antivirus), and a polished web UI — all in one Docker Compose stack. Google Workspace charges $6/user/month ($72/year). Mailcow self-hosted is free for unlimited users on your own hardware. The DNS setup takes 30 minutes; after that, you have a production-grade email server.

Key Takeaways

  • Mailcow: GPL 2.0, ~8K stars — Postfix + Dovecot + SOGo + Rspamd in one Docker stack
  • DNS critical: SPF, DKIM, DMARC, and PTR records are required for email deliverability
  • SOGo webmail: Full webmail interface with calendar (CalDAV) and contacts (CardDAV)
  • Rspamd: AI-powered spam filter with web UI — much better than SpamAssassin
  • Email aliases: Unlimited aliases per domain, catchall addresses, per-mailbox limits
  • ClamAV optional: Disable on low-RAM servers (< 2GB) — uses ~700MB RAM

Prerequisites

  • VPS with static IP — cloud server at Hetzner, DigitalOcean, Vultr, etc.
  • Minimum specs: 2 CPU, 4GB RAM (6GB recommended if enabling ClamAV)
  • Port 25 unblocked — many residential ISPs block port 25; VPS providers usually allow it
  • A domain you control — e.g., yourdomain.com
  • Reverse DNS (PTR) record — set in your VPS control panel to match your hostname

Part 1: Docker Setup

# Install Mailcow (official installer):
cd /opt
git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized

# Generate configuration:
./generate_config.sh
# Prompts for:
# - Hostname: mail.yourdomain.com
# - Timezone: America/Los_Angeles

# Review mailcow.conf:
cat mailcow.conf

Key settings in mailcow.conf:

# mailcow.conf
MAILCOW_HOSTNAME=mail.yourdomain.com
TZ=America/Los_Angeles

# Disable ClamAV on low-RAM servers:
SKIP_CLAMD=y

# Skip unbound DNS resolver (use host DNS):
SKIP_UNBOUND=y

# HTTP/HTTPS ports (if Caddy/Nginx handles TLS):
HTTP_PORT=8080
HTTPS_PORT=8443
HTTP_BIND=127.0.0.1
HTTPS_BIND=127.0.0.1
# Start Mailcow:
docker compose pull
docker compose up -d

# Check all services are running:
docker compose ps

Services that start:

  • mailcow-postfix — SMTP (25, 587, 465)
  • mailcow-dovecot — IMAP/POP3 (143, 993, 110, 995)
  • mailcow-rspamd — Spam filtering
  • mailcow-sogo — Webmail
  • mailcow-nginx — Internal reverse proxy
  • mailcow-mysql — Database
  • mailcow-redis — Cache
  • mailcow-clamd — Antivirus (if enabled)

Part 2: Reverse Proxy with Caddy

mail.yourdomain.com {
    reverse_proxy localhost:8080 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

Visit https://mail.yourdomain.com — log in with admin / moohoo (change immediately).


Part 3: DNS Configuration

DNS is the most critical part of email deliverability. Configure all of these:

A and MX records

; A record for your mail server:
mail.yourdomain.com.    IN A     YOUR.SERVER.IP

; MX record — points to your mail server:
yourdomain.com.         IN MX 10 mail.yourdomain.com.

SPF record (prevents spoofing)

yourdomain.com.   IN TXT  "v=spf1 mx ~all"

This says: only servers listed in MX are allowed to send email for this domain.

DKIM record (cryptographic signing)

# Get your DKIM public key from Mailcow UI:
# Configuration → Domains → yourdomain.com → DKIM → Show public key
; DKIM record (long TXT record from Mailcow UI):
dkim._domainkey.yourdomain.com.  IN TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9..."

DMARC record (policy enforcement)

; Start with p=none (monitoring only), upgrade after confirming delivery:
_dmarc.yourdomain.com.  IN TXT  "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com"

; After 2-4 weeks, upgrade to quarantine:
_dmarc.yourdomain.com.  IN TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"

; After confirming no legitimate email is failing, upgrade to reject:
_dmarc.yourdomain.com.  IN TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com"

Reverse DNS (PTR) — set in VPS control panel

YOUR.SERVER.IP → mail.yourdomain.com

Autoconfiguration records (for email clients)

; Mozilla Thunderbird/GNOME Evolution autodiscover:
autoconfig.yourdomain.com.  IN CNAME  mail.yourdomain.com.

; Outlook autodiscover:
autodiscover.yourdomain.com.  IN CNAME  mail.yourdomain.com.

; SRV records for IMAP/SMTP:
_imap._tcp.yourdomain.com.    IN SRV  0 1 143  mail.yourdomain.com.
_imaps._tcp.yourdomain.com.   IN SRV  0 1 993  mail.yourdomain.com.
_submission._tcp.yourdomain.com.  IN SRV  0 1 587  mail.yourdomain.com.
_submissions._tcp.yourdomain.com. IN SRV  0 1 465  mail.yourdomain.com.

Test your DNS

# Verify all records:
dig MX yourdomain.com
dig TXT yourdomain.com     # SPF
dig TXT dkim._domainkey.yourdomain.com  # DKIM
dig TXT _dmarc.yourdomain.com           # DMARC
dig -x YOUR.SERVER.IP                   # PTR/reverse DNS

# Send a test email and check score:
# mail-tester.com — aim for 10/10

Part 4: Admin UI — Domains and Mailboxes

Add a domain

  1. Configuration → Domains → Add domain
  2. Domain: yourdomain.com
  3. Max mailboxes, quota, aliases
  4. Enable DKIM: generate 2048-bit key → copy TXT record to DNS

Create mailboxes

  1. Configuration → Mailboxes → Add mailbox
  2. Username: hello (creates hello@yourdomain.com)
  3. Quota: 10GB (or 0 for unlimited)
  4. Active: yes

Create aliases

# Example aliases via UI:
# postmaster@yourdomain.com → hello@yourdomain.com
# abuse@yourdomain.com → hello@yourdomain.com
# info@yourdomain.com → hello@yourdomain.com

# Catchall — catch all unmatched addresses:
# @yourdomain.com → hello@yourdomain.com

Part 5: Email Clients

Desktop (Thunderbird)

Autoconfiguration usually works automatically:

  1. Thunderbird → Add account
  2. Enter name, email, password
  3. Thunderbird auto-discovers IMAP/SMTP settings

Manual settings:

IMAP: mail.yourdomain.com:993 (SSL/TLS)
SMTP: mail.yourdomain.com:587 (STARTTLS)
Username: hello@yourdomain.com

iOS / macOS

  1. Settings → Mail → Add Account → Other
  2. Add Mail Account → name, email, password
  3. IMAP server: mail.yourdomain.com, SSL, port 993
  4. SMTP server: mail.yourdomain.com, STARTTLS, port 587

Webmail (SOGo)

Access at https://mail.yourdomain.com/SOGo — full webmail with:

  • Email composer with rich text
  • Calendar (CalDAV — sync to iOS/macOS/Android)
  • Address book (CardDAV)

Part 6: Rspamd (Spam Filtering)

Rspamd is Mailcow's spam engine — far superior to SpamAssassin:

Access Rspamd web UI

https://mail.yourdomain.com/rspamd

Default password in mailcow.confRSPAMD_PASSWORD

Train the spam filter

# Mark email as spam (trains Rspamd):
# In webmail: move message to Junk folder → Rspamd learns

# Via CLI:
docker exec -it mailcow-rspamd rspamc learn_spam < /path/to/spam.eml
docker exec -it mailcow-rspamd rspamc learn_ham < /path/to/legitimate.eml

Spam action thresholds

ScoreAction
< 5Deliver normally
5-10Add [SPAM] header, deliver
> 10Reject
GreylistingHold 5 min then retry

Adjust in Configuration → Configuration files → rspamd/local.d/actions.conf:

reject = 15;
add_header = 6;
greylist = 4;

Part 7: Backup and Restore

# Backup script (add to cron):
cd /opt/mailcow-dockerized

# Export all data:
./helper-scripts/backup_and_restore.sh backup all

# Backup created in: /opt/mailcow-dockerized/backups/

# Restore:
./helper-scripts/backup_and_restore.sh restore

# Manual MySQL backup:
docker exec mailcow-mysql mysqldump -u root --password="$(grep DBPASS mailcow.conf | cut -d= -f2)" mailcow \
  | gzip > mailcow-db-$(date +%Y%m%d).sql.gz

# Backup mail data (Dovecot volumes):
tar -czf mailcow-mail-$(date +%Y%m%d).tar.gz \
  $(docker volume inspect mailcow-dockerized_vmail-vol-1 --format '{{.Mountpoint}}')

Part 8: Maintenance and Updates

# Update Mailcow (run monthly):
cd /opt/mailcow-dockerized
./update.sh

# Check mail queue:
docker exec mailcow-postfix mailq

# Flush the queue:
docker exec mailcow-postfix postfix flush

# Postfix logs:
docker compose logs postfix -f

# Dovecot logs (IMAP):
docker compose logs dovecot -f

# Check TLS/certificate expiry:
docker exec mailcow-postfix openssl s_client -connect mail.yourdomain.com:993 </dev/null 2>&1 | grep -E "notAfter|subject"

Deliverability Checklist

Before sending to real recipients, verify:

  • MX record resolves to your server IP
  • PTR/reverse DNS matches your hostname
  • SPF includes your server
  • DKIM key published and signing works
  • DMARC policy set (start with p=none)
  • Port 25 not blocked by VPS provider
  • mail-tester.com score: 9+/10
  • Not on blocklists: check at mxtoolbox.com/blacklists

See all open source email tools at OSSAlt.com/categories/email.

Comments