A self-hosted, unified webmail client. Connect multiple IMAP/SMTP accounts and manage them all in one clean interface.
Quick Start · Setup Guide · Contributing · Roadmap
MailFlow is dual-licensed:
- AGPL-3.0 — free for personal use, self-hosting, and open-source projects. If you modify and distribute or host MailFlow, you must publish your changes under the same license.
- Commercial License — $500 per installation, one-time. For businesses or deployments where AGPL obligations cannot be met. Purchase here.
Personal self-hosting is free and always will be. This licensing model exists to protect the project from commercial exploitation while keeping MailFlow freely available to individuals and families.
If you contribute code, please read the Contributor License Agreement. By submitting a pull request you agree to its terms.
- Unified inbox — all accounts merged in one view, sorted by date
- Email categorization — automatic inbox tabs (Primary, Newsletters, Social, Notifications, Other) sort incoming mail by type using header detection and sender heuristics; AI reclassify button for misclassifications
- Unsubscribe — one-click unsubscribe button appears in the message pane for detected newsletters; sends the request or opens the unsubscribe URL automatically
- Conversation threads — messages grouped into reply chains with inline sent replies
- Rich text compose — WYSIWYG editor with font family, size, color, highlight, tables, emoji, links, attachments, image resize handles, and Excel table paste
- Attachments — send and receive file attachments across all accounts
- Multiple layouts — classic, compact, wide reader, vertical split, and more
- Multiple themes — dark, light, and several color schemes
- Multi-language UI — English, French, Spanish, Italian, German, Russian, and Simplified Chinese
- Full-text search — across all connected accounts simultaneously
- Real-time notifications — WebSocket-powered new-mail toasts and web push notifications
- PWA — installable as a desktop or mobile app with push notification support
- Command palette — Cmd+K / Ctrl+K quick-access for actions and navigation
- Keyboard shortcuts — full shortcut set, fully customisable per user
- Smart contact autocomplete — learns from sent mail to rank suggestions
- Reply / Forward / Compose — correct per-account SMTP routing; font family groups, email priority
- Folder navigation — expand any account to browse folders
- Star, archive, delete, mark read/unread — synced back to IMAP
- Inbox rules — automate actions (move, archive, delete, mark read, star) based on sender, subject, recipient, headers, body, or attachments
- Block list — automatically move mail from blocked senders to trash before inbox rules run
- Spam reporting — mark messages as spam or not spam from the context menu, toolbar, or bulk actions; feedback will feed into automated filtering in a future release
- Snooze — snooze messages until a chosen time; they reappear at the top of the inbox
- AI assistant — connect any OpenAI-compatible provider (local models or cloud); summarise threads, draft replies, ask questions about a message
- Password recovery — recover your account via a recovery email address configured in profile settings
- User management — admin panel, invite-only registration, invite emails
- Two-factor authentication — TOTP (any authenticator app), email OTP fallback, persistent device trust; admin-configurable enforcement policy
- SSO / OIDC — single sign-on via any OpenID Connect provider
- Microsoft 365 / OAuth2 — work accounts via Azure App Registration; personal Outlook.com via device code flow
- Todoist integration — create tasks directly from emails; tasks include a deep link back to the original message
- CardDAV — expose your MailFlow contacts as a CardDAV address book for sync with phone and desktop contact apps
![]() Default dark theme |
![]() Light theme |
![]() Catppuccin theme |
![]() Gruvbox theme |
![]() Compose window |
![]() Collapsed sidebar |
![]() Account management |
![]() Folder navigation |
![]() Layout options |
![]() Appearance settings |
There are three ways to run MailFlow. The pre-built image method is recommended for most users.
No cloning or building required. Docker pulls the pre-built images directly from GHCR.
- A server with Docker and Docker Compose installed
curl -o docker-compose.yml https://raw.githubusercontent.com/maathimself/mailflow/main/docker-compose.ghcr.yml
curl -o .env https://raw.githubusercontent.com/maathimself/mailflow/main/.env.exampleEdit .env — the required fields are:
| Variable | Description |
|---|---|
APP_URL |
Full URL, e.g. https://mail.example.com |
SESSION_SECRET |
openssl rand -hex 32 |
DB_PASSWORD |
openssl rand -hex 16 |
ENCRYPTION_KEY |
openssl rand -hex 32 |
docker compose up -dMailFlow will be available on port 443 (HTTPS, self-signed certificate) and port 80 (HTTP).
Ports are configurable in .env:
| Variable | Default | Description |
|---|---|---|
APP_PORT |
443 |
HTTPS port |
APP_HTTP_PORT |
80 |
HTTP port |
Optional — automatic HTTPS via Let's Encrypt: set DOMAIN and ACME_EMAIL in .env, download the HTTPS overlay, then restart:
curl -o docker-compose.https.yml https://raw.githubusercontent.com/maathimself/mailflow/main/docker-compose.https.yml
docker compose -f docker-compose.yml -f docker-compose.https.yml --profile https up -dThis adds a Caddy reverse proxy that handles certificate issuance and renewal automatically. Requires Docker Compose 2.21+, a public domain with DNS pointing at the server, and ports 80/443 open.
Optional — behind your own reverse proxy: point your proxy at port 80. Set APP_HTTP_PORT in .env if you need a different host port. Your proxy should forward X-Forwarded-Proto: https so that session cookies are marked Secure correctly.
Open https://your-domain.com in a browser. The first account registered becomes
the admin. After registering, you can close registration and manage users from the
settings panel → Users tab.
In the settings panel → Accounts → Add Account. Select a preset (Gmail, iCloud) or Custom for any IMAP server.
docker compose pull
docker compose up -dTo pin to a specific version instead of latest, add MAILFLOW_VERSION=1.9.0 to your .env.
- A server with Docker and Docker Compose installed
git clone https://github.com/maathimself/mailflow.git mailflow
cd mailflowcp .env.example .envEdit .env — the required fields are:
| Variable | Description |
|---|---|
APP_URL |
Full URL, e.g. https://mail.example.com |
SESSION_SECRET |
openssl rand -hex 32 |
DB_PASSWORD |
openssl rand -hex 16 |
ENCRYPTION_KEY |
openssl rand -hex 32 |
docker compose up -d --buildFirst build takes 2–3 minutes. MailFlow will be available on port 443 (HTTPS, self-signed certificate) and port 80 (HTTP).
Optional — automatic HTTPS via Let's Encrypt: set DOMAIN and ACME_EMAIL in .env, then start with the HTTPS overlay (requires Docker Compose 2.21+):
docker compose -f docker-compose.yml -f docker-compose.https.yml --profile https up -d --buildOptional — behind your own reverse proxy: point your proxy at port 80. Your proxy should forward X-Forwarded-Proto: https so that session cookies are marked Secure correctly.
Open https://your-domain.com in a browser. The first account registered becomes
the admin. After registering, you can close registration and manage users from the
settings panel → Users tab.
In the settings panel → Accounts → Add Account. Select a preset (Gmail, iCloud) or Custom for any IMAP server.
Run MailFlow directly on any Linux, macOS, or BSD machine using Node.js, PostgreSQL, and Redis. No container runtime required. The steps below use Ubuntu/Debian; adapt package manager commands for other platforms.
- Node.js 20+ — nodejs.org or via your package manager
- PostgreSQL 16+
- Redis 7+
- nginx — serves the built frontend and proxies API/WebSocket requests to the backend
Ubuntu / Debian:
# Node.js 20 via NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs postgresql redis-server nginxmacOS (Homebrew):
brew install node@20 postgresql@16 redis nginx
brew services start postgresql@16
brew services start redissudo -u postgres psql <<'SQL'
CREATE USER mailflow WITH PASSWORD 'replace-with-a-strong-password';
CREATE DATABASE mailflow OWNER mailflow;
SQLgit clone https://github.com/maathimself/mailflow.git /opt/mailflow
cd /opt/mailflowcp .env.example .envEdit .env. In addition to the required secrets, set these for a native install:
| Variable | Value |
|---|---|
APP_URL |
Full URL, e.g. https://mail.example.com |
SESSION_SECRET |
openssl rand -hex 32 |
DB_HOST |
localhost |
DB_NAME |
mailflow |
DB_USER |
mailflow |
DB_PASSWORD |
password you set in step 2 |
REDIS_URL |
redis://localhost:6379 |
ENCRYPTION_KEY |
openssl rand -hex 32 |
cd /opt/mailflow/frontend
npm ci
npm run build
# Built files are written to /opt/mailflow/frontend/distcd /opt/mailflow/backend
npm ci --omit=devA ready-to-use nginx config is provided in contrib/nginx.conf. Copy it, update the root path, then enable it:
sudo mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
sudo cp /opt/mailflow/contrib/nginx.conf /etc/nginx/sites-available/mailflowOpen /etc/nginx/sites-available/mailflow and replace /path/to/mailflow/frontend/dist with /opt/mailflow/frontend/dist.
The provided config listens on port 80 for use behind a TLS-terminating reverse proxy (Nginx/Caddy/Traefik). If you want nginx to terminate TLS directly, uncomment the HTTPS server block in the file and set your certificate paths. A quick self-signed cert:
sudo mkdir -p /etc/ssl/mailflow
sudo openssl req -x509 -nodes -newkey rsa:4096 -days 3650 \
-keyout /etc/ssl/mailflow/key.pem \
-out /etc/ssl/mailflow/cert.pem \
-subj "/CN=mailflow"Enable the site and reload nginx:
sudo ln -sf /etc/nginx/sites-available/mailflow /etc/nginx/sites-enabled/mailflow
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginxOption A — systemd (recommended for production):
sudo cp /opt/mailflow/contrib/mailflow.service /etc/systemd/system/mailflow.service
# Edit the service file if your install path or user differs from the defaults
sudo systemctl daemon-reload
sudo systemctl enable --now mailflow
sudo systemctl status mailflowOption B — PM2:
sudo npm install -g pm2
cd /opt/mailflow/backend
pm2 start src/index.js --name mailflow
pm2 save
pm2 startup # follow the printed command to register auto-start on bootOption C — foreground (testing only):
cd /opt/mailflow/backend
node src/index.jsOpen the app in a browser. The first account registered becomes the admin. After registering, close open registration from Settings → Users.
In the settings panel → Accounts → Add Account.
cd /opt/mailflow
git pull
cd frontend && npm ci && npm run build && cd ..
cd backend && npm ci --omit=dev && cd ..
sudo systemctl restart mailflow # or: pm2 restart mailflowGmail requires an App Password (not your normal password):
- Enable 2-step verification on your Google account
- Go to myaccount.google.com/apppasswords
- Create a new App Password — name it "MailFlow"
- Use the 16-character password in the MailFlow account form
| Setting | Value |
|---|---|
| IMAP Host | imap.gmail.com |
| IMAP Port | 993 |
| SMTP Host | smtp.gmail.com |
| SMTP Port | 587 |
| Username | your Gmail address |
- Go to appleid.apple.com → Sign-In and Security → App-Specific Passwords
- Generate a password — name it "MailFlow"
| Setting | Value |
|---|---|
| IMAP Host | imap.mail.me.com |
| IMAP Port | 993 |
| SMTP Host | smtp.mail.me.com |
| SMTP Port | 587 |
| Username | your full iCloud email ([email protected]) |
Work / school accounts (Microsoft 365): require modern auth via Azure App Registration:
- In MailFlow settings → Integrations → Microsoft 365 — follow the Azure App Registration instructions shown there
- After saving the config, click Connect Microsoft account
Personal accounts (Outlook.com / Hotmail): no Azure registration needed — uses the device code flow:
- Settings → Accounts → Add Account → Outlook.com / Hotmail
- MailFlow displays a short code — visit microsoft.com/devicelogin and enter it to authorise
Any standard IMAP/SMTP server works. Use port 993 for IMAP (TLS) and 587 (STARTTLS) or 465 (TLS) for SMTP.
# View all logs
docker compose logs -f
# View backend logs only
docker compose logs -f backend
# Stop
docker compose down
# Stop and delete all data (destructive)
docker compose down -v
# Update to latest images (pre-built install)
docker compose pull && docker compose up -d
# Rebuild after a code change (Docker build-from-source install)
docker compose up -d --build
# Update a native install
git pull && \
cd frontend && npm ci && npm run build && cd .. && \
cd backend && npm ci --omit=dev && cd .. && \
sudo systemctl restart mailflow # or: pm2 restart mailflow# Backup database
docker exec mailflow-postgres pg_dump -U mailflow mailflow \
> mailflow-$(date +%Y%m%d).sql
# Restore database
cat mailflow-YYYYMMDD.sql | \
docker exec -i mailflow-postgres psql -U mailflow -d mailflowBrowser (HTTPS / HTTP)
│
▼
nginx (frontend container — ports 443 + 80)
│
├── /api/* → Node.js backend (port 3000)
├── /oauth/ → Node.js backend (port 3000)
└── /ws → Node.js backend WebSocket (port 3000)
│
├── PostgreSQL (messages, accounts, users)
├── Redis (sessions)
└── IMAP (outbound to mail servers)
nginx and the backend communicate on an internal Docker network. PostgreSQL and Redis are not exposed outside that network.
Browser (HTTPS)
│
▼
Your proxy (Nginx / Traefik / Caddy / etc. — TLS termination)
│ X-Forwarded-Proto: https
▼
nginx (frontend container — port 80)
│
└── backend, PostgreSQL, Redis (internal network, unchanged)
Browser (HTTPS)
│
▼
Caddy (ports 80/443 — TLS termination, auto Let's Encrypt)
│
▼
nginx (frontend container — internal only)
│
└── backend, PostgreSQL, Redis (internal network, unchanged)
MailFlow is free and open source. If it's useful to you, consider supporting development:
No manual migration steps required. All schema changes apply automatically on first startup.
ENCRYPTION_KEY is now required at startup. The server will refuse to start if the variable is missing or not exactly 64 hex characters. Generate one with openssl rand -hex 32 before upgrading if you have not already set this.
Two database migrations (0019_user_integrations, 0020_mfa_device_trust) run automatically on startup. No manual steps required.
2FA is off by default. Existing users are unaffected unless an admin enables enforcement under Settings → Security.
Breaking change for accounts with "Skip TLS verification" enabled.
An earlier release introduced an admin-controlled connection policy (Settings → Security → Mail Server Connection Policy). TLS verification is now enforced by default at the server level.
If any accounts were configured with Skip TLS verification (e.g. for a self-signed certificate on a local IMAP server), those accounts will stop syncing after upgrading from an older version. To restore connectivity, an admin must enable Allow insecure TLS in Settings → Security before or immediately after deploying.
- The first registered user becomes the admin automatically
- Close open registration in Settings → Users once you've set up your accounts
- Use the invite system to onboard additional users
- Enable two-factor authentication in Settings → Security — supports TOTP (authenticator app), email OTP fallback, and persistent device trust. TOTP codes are one-time and cannot be replayed within their validity window
- Session cookies are
HttpOnly,SameSite=Lax, with a 7-day TTL. TheSecureflag is set automatically when the connection is HTTPS (direct or via a proxy that forwardsX-Forwarded-Proto: https) - Passwords are bcrypt-hashed (cost factor 12)
- Login and registration endpoints are rate-limited (10 attempts per 15 minutes per IP)
- Password reset tokens are consumed atomically — concurrent reset requests cannot both succeed
- Database and Redis are not exposed outside the Docker network
- IMAP/SMTP credentials are stored at rest in the database (standard for webmail clients — protect access to your server and database volume accordingly)
- All responses include
X-Frame-Options: DENYandReferrer-Policy: same-originsecurity headers - Email HTML is sanitized before rendering, including stripping external
url()references from CSS style blocks to prevent tracking










