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

35 stars

Watchers

0 watching

Forks

Latest commit

 

History

97 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ManyMail

Lightweight self-hosted email server: SMTP, webmail, forwarding rules and a REST API in one Go binary with SQLite. Disposable / temp mail and multi-domain inboxes without Postfix or Mailcow.

ManyMail — lightweight self-hosted email server

One container, about 10 MB of RAM — SMTP receiver, webmail, rules, REST API

Go SQLite 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 server 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 + forwarding rules, one compose file ManyMail
Small VPS or ARM box, no Postfix/Dovecot ManyMail
Company mail, IMAP for desktop clients, CalDAV, ActiveSync, Rspamd Mailcow / docker-mailserver / Mail-in-a-Box

Features

  • Self-hosted SMTP — receive mail for your domains on port 25 (STARTTLS optional); a built-in DNS check tells you which MX, SPF and DMARC records are missing
  • Webmail — search, read, reply, compose and send through Resend; incoming HTML is sanitized and remote images stay blocked until you load them
  • External mailboxes — an optional bridge shows Gmail, Outlook, QQ, 163 and other IMAP accounts in the same web UI
  • 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; rules run the moment mail arrives and are edited on a Rules page (docs)
  • Multi-domain — any number of domains; mailboxes can be created on first use
  • Small — one static binary (~15 MB), one SQLite file, ~10 MB of RAM in use; prebuilt amd64 / arm64 images on GHCR. MIT licensed, data stays on your server

Overview

Three containers in one compose file:

Container Stack Port Description
manymail Go + SQLite :25 :8080 :5000 SMTP receiver, REST API, web UI and rules engine
imap-bridge Node.js + imapflow :3939 (internal) Optional: external IMAP accounts in the web UI
caddy Caddy 2 :80 :443 HTTPS with automatic certificates

Architecture

    Internet                              Your Server
    ────────                              ───────────
                      ┌────────────────────────────────────────────────┐
   Incoming     ──────┤►  manymail (one Go process)                    │
   Email              │   ┌──────────────┐   ┌───────────────────────┐ │
   (SMTP :25)         │   │ SMTP receiver│──►│ SQLite                │ │
                      │   │ Rules engine │◄─►│ mail, attachments,    │ │
                      │   │ REST API     │   │ accounts, rules       │ │
                      │   │ Web UI       │   └───────────────────────┘ │
                      │   └──────▲───────┘                             │
   Browser    ──► Caddy (HTTPS) ─┘   ▲ new-mail events                 │
                      │              │                                 │
                      │   imap-bridge (Node.js) ──► Gmail / Outlook /  │
                      │                             QQ / 163 ...       │
                      └────────────────────────────────────────────────┘

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. The server refuses to start in ENVIRONMENT=production while a secret is left at its default.

Check

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

Update

git fetch --depth 1 origin tag vX.Y.Z && git checkout vX.Y.Z
sed -i 's/^MANYMAIL_VERSION=.*/MANYMAIL_VERSION=X.Y.Z/' .env
docker compose pull && docker compose up -d

Upgrading from 1.x (MongoDB)

Version 2 replaces the Python services and MongoDB with one Go binary and SQLite. The .env stays as it is. Export the data before pulling the new version, while MongoDB still runs:

mkdir -p export
for c in domains accounts messages sent_messages sender_keys routing_rules routing_events; do
  docker compose exec -T mongodb mongoexport --quiet --db mailserver --collection "$c" > "export/$c.json"
done
git pull                                   # or check out the 2.x tag
docker compose run --rm -v "$PWD/export:/export:ro" manymail import /export
docker compose up -d --remove-orphans      # removes the old mail-service, mail-viewer, router, imap-server, mongodb

Message ids, mailbox passwords, Resend keys saved in the web UI and rules carry over; imported mail is not forwarded again. The IMAP server for desktop clients (port 993) is gone in 2.x. Once everything looks right, remove the old MongoDB volume with docker volume rm.


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 manymail dnscheck) 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: put the certificate and key in ./certs (mounted read-only) and set SMTP_TLS_CERT / SMTP_TLS_KEY in .env. See .env.example.


API Reference

Base URL: http://127.0.0.1:8080

Auth: Authorization: Bearer <token> for mailbox endpoints; X-API-Key: <API_KEY> for /admin/*

Account

POST /accounts                              # Create mailbox account
POST /token                                 # Login, returns JWT (24h)

Messages

GET    /messages                            # Inbox (paginated: ?offset=0&limit=30)
GET    /messages/{id}                       # Message detail, marks it read
GET    /messages/{id}/attachments/{aid}     # Download an attachment
GET    /messages/search?q=                  # Search subject, sender and body
DELETE /messages/{id}                       # Move to trash
GET    /messages/trash                      # Trash
POST   /messages/{id}/restore               # Restore from trash
DELETE /messages/{id}/permanent             # Delete for good
POST   /messages/batch                      # {"action": "delete|mark_read|restore|permanent_delete", "ids": [...]}
GET    /sent                                # Sent messages

System

GET  /health                                # Health check (no auth)
GET  /domains                               # Active domains
GET  /admin/domains · POST · DELETE /{d}    # Manage domains (API key)
GET  /admin/dns-check                       # DNS self-check (API key)

The rules API (list, edit, dry-run, test) lives under /router/* with the API key; see docs/mail-router.md.

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/
├── main.go                 # Config (.env) and startup; `manymail import`, `manymail dnscheck`
├── smtpd.go, parse.go      # SMTP receiver and MIME parsing
├── store.go, routestore.go # SQLite schema and queries
├── api.go                  # DuckMail-compatible REST API
├── viewer.go, sanitize.go  # Web UI, HTML sanitizer, image proxy, sending via Resend
├── rules.go, router.go     # Forwarding / push rules and the rules API
├── dnscheck.go             # MX / SPF / DMARC self-check
├── migrate.go              # Import from a 1.x MongoDB export
├── web/                    # Templates and front-end files, embedded into the binary
├── frontend/               # Main mail UI (SolidJS + TypeScript, Vite) → web/static/app
├── imap-bridge/            # Optional Node.js bridge to external IMAP accounts
├── i18n/                   # UI translations (tools/build-i18n.cjs bundles them)
├── Dockerfile              # Node builds the UI, Go builds the binary → alpine, runs as uid 10001
├── docker-compose.yml      # manymail + imap-bridge + caddy
├── install.sh              # Interactive installer
└── tools/                  # UI tests, deploy script, test senders

Security

Layer Feature
Auth JWT tokens (24h expiry) + API key for admin endpoints
Password bcrypt hashing
Rate Limit Per-IP throttling on API, SMTP, web login, auto-create, domain admin, mail changes and sending
SMTP IP / sender blacklist, greylist, recipient and size limits, optional STARTTLS, auto-reply loop guard
Email Render Whitelist HTML/CSS sanitizer, sandboxed iframe, remote images only on request
Network Image proxy checks every connection and redirect against private addresses
Storage One SQLite file; optional cleanup after MESSAGE_TTL_DAYS (default 3, 0 keeps mail forever)
External IMAP Saved accounts are encrypted with IMAP_ACCOUNT_ENCRYPTION_KEY
Web Login-protected UI, HttpOnly session cookies, JSON-only APIs, changing ACCESS_PASSWORD ends all sessions

Additional hardening guidance: docs/production-hardening.md.


Tech Stack

Component Built with
manymail Go 1.27 · emersion/go-smtp · modernc.org/sqlite (pure Go, no cgo) · golang.org/x/net/html · x/crypto/bcrypt
web UI SolidJS 2 (RC) · TypeScript · Vite (build time only) · Bootstrap 5 · Quill
imap-bridge Node.js 20 · express · imapflow · mailparser
HTTPS Caddy 2

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

35 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages