A self-hosted, unified webmail client. Connect multiple IMAP/SMTP accounts (Gmail, iCloud, Outlook, custom) and read them all in one clean interface.
Please open an issue here if you encounter a problem. See the Security Notes section before deploying.
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
- Multiple layouts — classic, compact, wide reader, vertical split, and more
- Multiple themes — dark, light, and several color schemes
- Multi-language UI — English, French, Spanish, and Italian
- Full-text search — across all connected accounts simultaneously
- Real-time notifications — WebSocket-powered new-mail toasts and web push (PWA/browser)
- Reply / Forward / Compose — correct per-account SMTP routing
- Folder navigation — expand any account to browse folders
- Star, delete, mark read — synced back to IMAP
- User management — admin panel, invite-only registration, invite emails
- SSO / OIDC — single sign-on via any OpenID Connect provider
- Microsoft 365 / OAuth2 — for work accounts that require modern auth
![]() 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.0.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 (you@icloud.com) |
Work/school accounts that require modern authentication:
- In MailFlow settings → Integrations → Microsoft 365 — follow the Azure App Registration instructions shown there
- After saving the config, click Connect Microsoft account
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)
- 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 (TOTP) in Settings → Security for extra account protection
- 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)
- 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)










