genro-mail-proxy

logo

A microservice that decouples email delivery from your application.

What it does

genro-mail-proxy sits between your application and SMTP servers. Your application sends messages to the proxy via REST API; the proxy handles delivery with:

  • Persistent queue: Messages are stored in SQLite and survive restarts

  • Automatic retry: Failed deliveries are 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 are posted back to your application via HTTP callback

  • Bounce detection: IMAP polling for bounces with DSN parsing and hard/soft classification (BSL 1.1)

  • Multi-tenancy: Multiple organizations can share one instance with separate accounts (BSL 1.1)

Architecture

┌─────────────┐      REST       ┌──────────────────┐      SMTP      ┌─────────────┐
│ Application │ ──────────────► │ genro-mail-proxy │ ─────────────► │ SMTP Server │
└─────────────┘                 └──────────────────┘                └─────────────┘
       ▲                               │
       │                               │
       └───────────────────────────────┘
                delivery reports

The proxy exposes a FastAPI REST API secured by X-API-Token. Background loops handle SMTP delivery and delivery report callbacks.

When to use it

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 metrics

  • Retry logic is required without implementing it in every service

  • Multi-tenant isolation is needed for different organizations or environments

When NOT to use it

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

Command-line interface

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 dispatch

Each instance stores its configuration in ~/.mail-proxy/<name>/mail_service.db. The CLI supports both command-line arguments and interactive prompts for complex operations like adding tenants or accounts.

Quick start

Docker:

docker run -p 8000:8000 \
  -e GMP_API_TOKEN=your-secret-token \
  genro-mail-proxy

CLI:

pip install genro-mail-proxy
mail-proxy start myserver

Then configure a tenant, add an SMTP account, and start sending messages. See Installation and Usage for details.

Performance notes

When the proxy is under load:

  • 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 instead of base64 encoding

The SQLite database handles typical workloads but doesn’t scale well under high concurrency. For high-volume deployments, consider running multiple instances with separate databases.