A microservice that decouples email delivery from your application.
genro-mail-proxy sits between your application and SMTP servers. Your application sends messages to the proxy via REST API; the proxy handles delivery.
- Persistent queue: Messages stored in SQLite or PostgreSQL, survive restarts
- Automatic retry: Failed deliveries retried with exponential backoff
- Rate limiting: Per-account limits (minute/hour/day) shared across instances
- Priority queuing: Four levels (immediate, high, medium, low) with FIFO within each
- Delivery reports: Results posted back to your application via HTTP callback
- Connection pooling: SMTP connections pooled with acquire/release semantics
- Credential encryption: SMTP passwords encrypted at rest with AES-256-GCM
- Attachments: Multi-source fetching (base64, filesystem, HTTP, endpoint) with caching
- Prometheus metrics: Built-in monitoring via
/metricsendpoint - CLI tool: Full instance management without HTTP API
- Multi-tenancy: Multiple organizations share one instance with data isolation
- Bounce detection: IMAP polling for bounces with DSN parsing and hard/soft classification
- PEC support: Italian certified email (Posta Elettronica Certificata) with receipt tracking
- Large file handling: Auto-upload attachments to S3/GCS/Azure and replace with download links
┌─────────────┐ REST ┌──────────────────┐ SMTP ┌─────────────┐
│ Application │ ──────────────► │ genro-mail-proxy │ ─────────────► │ SMTP Server │
└─────────────┘ └──────────────────┘ └─────────────┘
▲ │
│ │
└───────────────────────────────┘
delivery reports
Consider this proxy when:
- Multiple application instances need shared rate limits for outbound email
- Email delivery should not block your application's main request flow
- Delivery tracking is needed with central logging and Prometheus metrics
- Retry logic is required without implementing it in every service
- Multi-tenant isolation is needed for different organizations or environments
This proxy adds operational complexity. Direct SMTP may be simpler when:
- You have a single application instance with low email volume
- Latency is acceptable (direct SMTP adds ~500-600ms per send)
- No retry logic is needed (transactional emails with immediate feedback)
- No rate limiting is required by your SMTP provider
- You prefer fewer moving parts in your infrastructure
Docker:
docker run -p 8000:8000 \
-e GMP_API_TOKEN=your-secret-token \
genro-mail-proxyCLI:
pip install genro-mail-proxy
mail-proxy start myserverThen configure a tenant, add an SMTP account, and start sending messages.
The mail-proxy CLI manages instances without going through the HTTP API:
# Instance management
mail-proxy list # List all instances
mail-proxy start myserver # Start an instance
mail-proxy stop myserver # Stop an instance
mail-proxy myserver info # Show instance details
# Tenant management
mail-proxy myserver tenants list # List tenants
mail-proxy myserver tenants add acme # Add a tenant (interactive)
# Account management (per tenant)
mail-proxy myserver acme accounts list # List SMTP accounts
mail-proxy myserver acme accounts add # Add account (interactive)
# Message operations
mail-proxy myserver acme messages list # List queued messages
mail-proxy myserver acme send email.eml # Send from .eml file
mail-proxy myserver acme run-now # Trigger immediate dispatchEach instance stores its configuration in ~/.mail-proxy/<name>/mail_service.db.
The CLI supports both command-line arguments and interactive prompts for complex operations.
The proxy exposes a FastAPI REST API secured by X-API-Token:
POST /commands/add-messages- Queue messages for deliveryGET /messages- List queued messagesPOST /commands/run-now- Trigger immediate dispatch cycleGET /accounts- List SMTP accountsGET /metrics- Prometheus metrics
See API Reference for details.
The proxy supports multiple attachment sources via explicit fetch_mode:
| fetch_mode | storage_path example | Description |
|---|---|---|
base64 |
base64:SGVsbG8gV29ybGQ= |
Inline base64-encoded content (requires base64: prefix) |
filesystem |
/tmp/file.pdf |
Local filesystem path |
endpoint |
doc_id=123 |
HTTP POST to tenant's attachment endpoint |
http_url |
https://storage.example.com/file.pdf |
HTTP GET from external URL |
A two-tiered cache (memory + disk) reduces redundant fetches. Filenames can include an MD5 hash marker (report_{MD5:abc123}.pdf) for cache lookup.
For attachments exceeding a size threshold, the proxy can upload them to external storage (S3, GCS, Azure, or local filesystem) and replace them with download links in the email body.
pip install genro-mail-proxy[enterprise-s3] # or [enterprise-gcs], [enterprise-azure]Configure per-tenant via large_file_config:
{
"enabled": true,
"max_size_mb": 10,
"storage_url": "s3://bucket/mail-attachments",
"action": "rewrite"
}Actions: warn (log only), reject (fail message), rewrite (upload and replace with link).
Configuration is managed via the CLI. Each instance stores its settings in ~/.mail-proxy/<name>/mail_service.db.
# Start an instance (creates it if new)
mail-proxy start myserver
# Add a tenant with attachment endpoint
mail-proxy myserver tenants add
# Interactive prompts: tenant_id, name, base_url, attachment_path, auth method
# Add an SMTP account
mail-proxy myserver acme accounts add
# Interactive prompts: account_id, host, port, user, password, TLS, rate limitsFor Docker deployments, use environment variables (prefixed with GMP_):
docker run -p 8000:8000 \
-e GMP_API_TOKEN=your-secret-token \
-e GMP_DB_PATH=/data/mail_service.db \
-v mail-data:/data \
genro-mail-proxySee Usage for all options.
SMTP credentials are encrypted at rest using AES-256-GCM. Configure the encryption key:
# Generate a key
python -c "from tools.encryption import generate_key; print(generate_key())"
# Set via environment variable
export MAIL_PROXY_ENCRYPTION_KEY="<base64-encoded-32-byte-key>"For Docker/Kubernetes, mount the key as a secret or pass via environment variable:
docker run -p 8000:8000 \
-e GMP_API_TOKEN=your-api-token \
-e MAIL_PROXY_ENCRYPTION_KEY=K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols= \
genro-mail-proxySee Security Documentation for key rotation, Kubernetes secrets, and best practices.
- Request latency: ~30ms to queue a message (vs ~600ms for direct SMTP)
- Throughput: Limited by SMTP provider rate limits, not the proxy
- Memory: Attachment content is held in memory during send; use HTTP endpoints for large files
The SQLite database handles typical workloads but doesn't scale well under high concurrency. For high-volume deployments, use PostgreSQL:
pip install genro-mail-proxy[postgresql]Then configure the DSN via environment variable or CLI.
pip install -e ".[dev]"
pytestThis project uses a dual-license model:
| License | Features |
|---|---|
| Apache 2.0 | Core functionality: message queue, retry, rate limiting, priority, delivery reports, attachments, SMTP pooling, REST API, CLI |
| BSL 1.1 | Multi-Tenancy, Bounce Detection, PEC Support, Large Files |
Apache 2.0 (LICENSE): Free for any use.
BSL 1.1 (LICENSE-BSL-1.1): Free for testing, development, and non-production. Production use requires a commercial license from Softwell S.r.l. Each version converts to Apache 2.0 four years after its release (e.g., v0.7.0 released Jan 2026 → Apache 2.0 on Jan 2030).
See NOTICE for full details.
Copyright 2025 Softwell S.r.l. — Genropy Team