Full email stack in a single Docker Compose. Your servers, your data, your rules. MIT licensed, free forever.
From zero to running in under 2 minutes. You need Docker and a domain with MX records pointing to your server.
$ git clone https://github.com/edwin0x/flashmail
$ cd spawnmail $ cp .env.example .env
# Edit .env with your settings
DOMAIN=mail.yourdomain.com
POSTGRES_PASSWORD=your-secure-password
API_SECRET_KEY=your-api-secret $ docker compose up -d
→ spawnmail-api running on :8000
→ spawnmail-smtp running on :25
→ spawnmail-db running on :5432
# Verify it's running
$ curl http://localhost:8000/health
→ { "status": "ok" } $ docker exec spawnmail-api \
spawnmail-admin create-key --name "my-agent"
→ fm_live_abc123...def456
# Test it
$ curl -H "x-api-key: fm_live_abc123...def456" \
http://localhost:8000/me
→ { "email": "admin", "inboxes_count": 0 } Three services, one Docker Compose. No Kubernetes, no orchestrators, no complexity.
Inbound Email
│
▼
┌──────────────────┐
│ Postfix SMTP │
│ (port 25) │
└────────┬─────────┘
│ pipe transport
▼
┌──────────────────┐
│ FastAPI App │ ←── REST API (port 8000)
│ (Python) │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ PostgreSQL │
│ (port 5432) │
└──────────────────┘ Production-grade MTA handles all inbound email. Pipe transport forwards parsed mail to the API layer. Handles DKIM, SPF, and DMARC if you configure DNS.
Python API server exposing REST endpoints. Handles inbox CRUD, email storage, long-polling, API key auth, and webhook dispatch.
All data lives in PostgreSQL. Inboxes, emails, API keys, raw MIME payloads. Foreign key cascades enable atomic zero-residue purges.
What you need before deploying.
Any Linux VPS with Docker installed. 1 vCPU, 1GB RAM minimum. We recommend 2 vCPU / 2GB for production.
A domain (or subdomain) with an MX record pointing to your server's IP. This is where inbound email gets routed.
Docker Engine 20.10+ and Docker Compose V2. That's it — no Kubernetes, no Terraform, no cloud provider lock-in.
SMTP requires port 25 to be open. Some cloud providers block it by default — check your firewall and hosting provider policies.
Environment variables control everything. No YAML files, no config directories.
| Variable | Description | Default |
|---|---|---|
DOMAIN | Your email domain (e.g., mail.example.com) | Required |
POSTGRES_PASSWORD | Database password | Required |
API_SECRET_KEY | Secret for JWT signing and key derivation | Required |
API_PORT | Port for the REST API | 8000 |
SMTP_PORT | Port for inbound SMTP | 25 |
MAX_INBOX_TTL | Maximum inbox lifetime in seconds | 2592000 (30d) |
MAX_EMAIL_SIZE | Maximum email size in bytes | 10485760 (10MB) |
WEBHOOK_TIMEOUT | Webhook delivery timeout in seconds | 10 |
Self-hosting is great. Sometimes managed is better. Here's how to decide.