Skip to content
margbug01Public

About

Lightweight self-hosted email server with Docker: SMTP, IMAP, webmail, REST API. Disposable/temp mail and multi-domain inboxes without Postfix.

Topics

Resources

Contributing

Security policy

Stars

32 stars

Watchers

0 watching

Forks

Repository files navigation

ManyMail

Lightweight self-hosted email server with Docker: SMTP, IMAP, webmail, and REST API. Disposable / temp mail and multi-domain inboxes without Postfix or Mailcow.

ManyMail — lightweight self-hosted email server

One docker compose up — SMTP receiver, webmail, IMAP, REST API

Python FastAPI Flask Node.js MongoDB Docker License: MIT Stars

Website · 中文文档


ManyMail webmail inbox — self-hosted email UI

All emails shown in the screenshot are for testing purposes only and have no real-world significance.

Why ManyMail

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

Features

  • Self-hosted SMTP — receive mail for your domain on port 25; a built-in DNS check tells you which MX, SPF and DMARC records are missing
  • 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
  • Forwarding and push rules — forward to Gmail/QQ via Resend, push to ntfy, Telegram or a webhook, tag / mark read / trash by recipient, sender or subject, edited on a Rules page; new mail in external IMAP accounts can be pushed too (docs)
  • Multi-domain — more than one domain; prefixes can be created when you need them
  • One-command install — install.sh plus prebuilt amd64 / arm64 images on GHCR; FastAPI + Flask + Node.js + MongoDB in Docker Compose. MIT licensed, data stays on your server

Overview

Four 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.
mail-router Python — Forwarding and push-notification rules for incoming mail

Architecture

    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                 │
                      └──────────────────────────────────────────────┘

Quick Start

You need a Linux server (amd64 or arm64) with Docker, a public IPv4 with inbound TCP 25, 80 and 443 open, and a domain whose DNS you control.

curl -fsSL https://raw.githubusercontent.com/margbug01/ManyMail/master/install.sh | bash

The installer asks for your mail domain(s), the host name for the web UI (default mail.<domain>), a password (or generates one) and how long to keep mail. It writes .env with random secrets, pulls the prebuilt images from GHCR (linux/amd64 and linux/arm64), starts everything, and finishes with a DNS check that prints the records still missing.

Unattended: MANYMAIL_DOMAINS=example.com ./install.sh --yes. All options are listed at the top of install.sh.

Manual setup

git clone https://github.com/margbug01/ManyMail.git && cd ManyMail
cp .env.example .env     # DOMAINS, SMTP_HOSTNAME, MAIL_WEB_HOST, secrets, ACCESS_PASSWORD
docker compose up -d     # pulls images; add --build to build from this checkout

For production, review docs/production-hardening.md and run python tools/check_production_config.py after editing .env.

Check

docker compose ps
docker compose exec -T mail-service python dnscheck.py   # also at https://<web host>/dns-check
curl http://127.0.0.1:8080/health

Update

git fetch --depth 1 origin tag v1.2.0 && git checkout v1.2.0
sed -i 's/^MANYMAIL_VERSION=.*/MANYMAIL_VERSION=1.2.0/' .env
docker compose pull && docker compose up -d

DNS Setup

Receiving needs two records; the other two are recommended:

; Required: where other servers deliver mail, and the host they connect to
yourdomain.com.         IN  MX   10  mail.yourdomain.com.
mail.yourdomain.com.    IN  A        <your-server-ip>

; Recommended: nobody may send as this domain
; (if you send through Resend, publish the SPF record Resend shows instead)
yourdomain.com.         IN  TXT      "v=spf1 -all"

; Recommended: DMARC policy
_dmarc.yourdomain.com.  IN  TXT      "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com"

ManyMail does not sign outgoing mail, so there is no DKIM key to generate here. Replies and forwards go out through Resend; add the DKIM and SPF records Resend lists for your domain.

The DNS self-check (/dns-check in the web UI, or dnscheck.py) looks up MX, the MX host's address, the web host, SPF and DMARC through public resolvers and prints the exact record to add for anything missing.

STARTTLS: ManyMail supports STARTTLS. Mount your TLS certificate and key via docker-compose.yml and set SMTP_TLS_CERT / SMTP_TLS_KEY in .env. See .env.example for details.


API Reference

Base URL: http://127.0.0.1:8080

Auth: Authorization: Bearer <token> (except /health, /token, /accounts)

Account

POST /accounts              # Create mailbox account
POST /token                 # Login, returns JWT

Messages

GET  /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 messages

System

GET  /health                # Health check (no auth required)
GET  /domains               # Active domain list
Example: 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"

Project Structure

ManyMail/
│
├── mail-service/                # SMTP + REST API
│   ├── app.py                   #   FastAPI main app
│   ├── dnscheck.py              #   DNS self-check (MX, SPF, DMARC)
│   ├── 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
│
├── router/                      # Forwarding / notification rules
│   ├── app.py                   #   Rule loop over new mail
│   ├── rules.py                 #   Matching and validation
│   ├── actions.py               #   forward, ntfy, telegram, webhook, tag, ...
│   └── cli.py                   #   list / add / dry-run / test rules
│
├── docker-compose.yml           # All services (GHCR images, or build from source)
├── install.sh                   # Interactive installer
├── .env.example                 # Environment variable template
└── tools/                       # Ops and manual test scripts
    ├── send_test_smtp.py        #   Send a test mail to local SMTP
    └── send_test_external_smtp.py # Send a test mail via external SMTP

Security

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.


Tech Stack


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

Community

ManyMail appreciates linux.do — a friendly Chinese tech community where people share ideas, projects, and practical self-hosting experience.


Changelog

CHANGELOG.md — see also the releases.

License

MIT


Built for self-hosting. Own your email infrastructure.

About

Lightweight self-hosted email server with Docker: SMTP, IMAP, webmail, REST API. Disposable/temp mail and multi-domain inboxes without Postfix.

Topics

Resources

Contributing

Security policy

Stars

32 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages