Lightweight self-hosted email server with Docker: SMTP, IMAP, webmail, and REST API. Disposable / temp mail and multi-domain inboxes without Postfix or Mailcow.
One docker compose up — SMTP receiver, webmail, IMAP, REST API
Website · 中文文档 · linux.do post
All emails shown in the screenshot are for testing purposes only and have no real-world significance.
Mailcow, docker-mailserver and Mail-in-a-Box are full mail platforms. ManyMail is a small Docker stack for inboxes you host yourself: verification-code mail, disposable addresses, and a few domains — with a browser UI and an API.
| You want | Use |
|---|---|
| Catch-all / temp addresses on your own domain | ManyMail |
| Webmail + REST API + IMAP, one compose file | ManyMail |
| Small VPS or ARM box, no Postfix/Dovecot | ManyMail |
| Company mail, CalDAV, ActiveSync, Rspamd | Mailcow / docker-mailserver / Mail-in-a-Box |
- Self-hosted SMTP — receive mail for your domain on port 25; MX, SPF, DKIM, DMARC records are documented
- Webmail — search, read, reply, compose; HTML is sanitized before it renders
- IMAP — Thunderbird and phone apps; optional bridge for Gmail, Outlook, QQ, 163
- REST API — DuckMail-compatible endpoints for scripts and disposable-mail tools
- Multi-domain — more than one domain; prefixes can be created when you need them
- Docker Compose — FastAPI + Flask + IMAP + MongoDB. MIT licensed, data stays on your server
Three services in one compose file:
| Service | Stack | Port | Description |
|---|---|---|---|
| mail-service | FastAPI + aiosmtpd | :25 :8080 |
SMTP receiver + DuckMail-compatible REST API |
| mail-viewer | Flask + bleach | :5000 |
Web mail viewer with search, reply & compose |
| imap-bridge | Node.js + imapflow | :3939 |
IMAP bridge for Gmail / Outlook / QQ etc. |
Internet Your Server
──────── ───────────
┌──────────────────────────────────────────────┐
│ │
Incoming ──────┤► mail-service ┌───────────────┐ │
Email │ (FastAPI+aiosmtpd) │ MongoDB 7 │ │
(SMTP :25) │ ┌──────────────┐ │ ┌─────────┐ │ │
│ │ SMTP Handler │────┤► │ accounts│ │ │
│ │ REST API │◄───┤ │ messages│ │ │
│ └──────┬───────┘ │ │ domains │ │ │
│ │ :8080 └───┴─────────┘─┘ │
│ │ │
│ ▼ │
Browser ────────┤► mail-viewer imap-bridge │
(HTTP :5000) │ (Flask) (Node.js) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Inbox View │ │ Gmail │ │
│ │ Search │◄───┤ Outlook │ │
│ │ Reply / Send │ │ QQ / 163 │ │
│ │ HTML Sanitize│ │ Yahoo / GMX │ │
│ └──────────────┘ └──────────────┘ │
│ :3939 │
└──────────────────────────────────────────────┘
git clone https://github.com/margbug01/ManyMail.git
cd ManyMail
cp .env.example .envEdit .env with your actual values:
# Mail Service
JWT_SECRET=your-strong-jwt-secret
API_KEY=your-api-key
SMTP_HOSTNAME=mail.yourdomain.com
DOMAINS=yourdomain.com
# Mail Viewer
ACCESS_PASSWORD=your-viewer-password
SECRET_KEY=random-flask-secret
UNIFIED_PASSWORD=shared-mailbox-password
AUTO_CREATE_ACCOUNTS=0
# Optional external IMAP bridge persistence
IMAP_ACCOUNT_PERSISTENCE=encrypted
IMAP_ACCOUNT_ENCRYPTION_KEY=use-a-strong-32-plus-character-keyFor production, review docs/production-hardening.md and run the configuration self-check after editing .env:
python tools/check_production_config.pydocker compose up -d# Check all services
docker compose ps
# View logs
docker compose logs -f
# Health check
curl http://127.0.0.1:8080/healthAdd these DNS records for your domain:
; MX record — tells other mail servers where to deliver
yourdomain.com. IN MX 10 mail.yourdomain.com.
; A record — points to your server IP
mail.yourdomain.com. IN A <your-server-ip>
; SPF record — declares which IPs may send for your domain
yourdomain.com. IN TXT "v=spf1 ip4:<your-server-ip> -all"
; DKIM record — email signature verification (generate key pair first)
default._domainkey.yourdomain.com. IN TXT "v=DKIM1; k=rsa; p=<your-public-key>"
; DMARC record — policy for failed SPF/DKIM checks
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"
STARTTLS: ManyMail supports STARTTLS. Mount your TLS certificate and key via
docker-compose.ymland setSMTP_TLS_CERT/SMTP_TLS_KEYin.env. See.env.examplefor details.
Base URL:
http://127.0.0.1:8080Auth:
Authorization: Bearer <token>(except/health,/token,/accounts)
POST /accounts # Create mailbox account
POST /token # Login, returns JWTGET /messages # List inbox (paginated: ?offset=0&limit=30)
GET /messages/{id} # Message detail
GET /messages/search?q= # Full-text search
PATCH /messages/{id} # Mark read / delete
GET /sent # Sent messagesGET /health # Health check (no auth required)
GET /domains # Active domain listExample: Create account & read inbox
# Create account
curl -X POST http://127.0.0.1:8080/accounts \
-H "Content-Type: application/json" \
-d '{"address": "user@yourdomain.com", "password": "secret123"}'
# Get token
TOKEN=$(curl -s -X POST http://127.0.0.1:8080/token \
-H "Content-Type: application/json" \
-d '{"address": "user@yourdomain.com", "password": "secret123"}' \
| jq -r '.token')
# List messages
curl http://127.0.0.1:8080/messages \
-H "Authorization: Bearer $TOKEN"ManyMail/
│
├── mail-service/ # SMTP + REST API
│ ├── app.py # FastAPI main app
│ ├── Dockerfile # Python 3.11-slim
│ └── requirements.txt # fastapi, aiosmtpd, pymongo, jwt, bcrypt
│
├── mail-viewer/ # Web Mail Viewer
│ ├── app.py # Flask main app
│ ├── Dockerfile # Python 3.11-slim + gunicorn
│ ├── requirements.txt # flask, bleach, tinycss2
│ ├── templates/
│ │ ├── index.html # Inbox UI
│ │ └── login.html # Login page
│ └── imap-mail-app/ # IMAP Bridge (Node.js)
│ ├── server.js # Express REST API
│ ├── client.js # ImapFlow wrapper
│ ├── config.js # Provider presets
│ └── package.json # imapflow, mailparser
│
├── docker-compose.yml # All 4 services orchestration
├── .env.example # Environment variable template
├── deploy.sh # Deployment script
├── test_smtp.py # SMTP tests
└── test_external_smtp.py # External SMTP tests
| Layer | Feature |
|---|---|
| Auth | JWT tokens (24h expiry) + API Key for admin endpoints |
| Password | bcrypt hashing |
| Rate Limit | Per-IP throttling on API, SMTP, viewer login, auto-create, domain admin, mail mutation, and send paths |
| SMTP | IP blacklist / greylist, recipient limits, size limits, optional STARTTLS |
| Email Render | HTML sanitization (bleach + CSSSanitizer), iframe sandbox |
| Network | Server-side image proxy with private-address rejection |
| Storage | Auto-cleanup via MongoDB TTL index (default 3 days; set MESSAGE_TTL_DAYS=0 to keep indefinitely) |
| External IMAP | Account persistence is encrypted with IMAP_ACCOUNT_ENCRYPTION_KEY; plaintext legacy account files are refused |
| Web | Login-protected viewer, HttpOnly session cookies, production-safe auto-create default |
Additional hardening guidance: docs/production-hardening.md.
Python 3.11 FastAPI · Flask |
Node.js 20 Express · ImapFlow |
MongoDB 7 pymongo |
Docker Compose |
| Component | Dependencies |
|---|---|
| mail-service | fastapi uvicorn aiosmtpd pymongo PyJWT bcrypt |
| mail-viewer | flask gunicorn requests bleach tinycss2 |
| imap-bridge | express imapflow mailparser dotenv |
ManyMail appreciates linux.do — a friendly Chinese tech community where people share ideas, projects, and practical self-hosting experience.
CHANGELOG.md — see also the releases.