Open Source · MIT · Rust

Burngate

Lightweight SMTP gateway that rejects spam at RCPT TO — before the message body is ever transmitted. Redis-backed, ~5 MB, single binary.

// How Burngate works

Internet ──▶ :25 burngate ──▶ Redis lookup

┌── not found ──▶ 550 reject (no body transferred)

└── found ──▶ accept DATA ──▶ relay to backend :2525

The problem

Most spam targets addresses that were never created. The typical SMTP server accepts the full email body, then checks the database — and discards it. You spent CPU, memory, and bandwidth for nothing.

The solution

SMTP announces the recipient at RCPT TO, before the body. Burngate checks Redis at that moment and rejects immediately if the address doesn't exist. Spam never transmits a single byte of body content.

Features

Pre-DATA filtering

Rejects unknown recipients before any body transfer

Redis-backed

Sub-millisecond mailbox existence checks

Configurable schema

Bring your own key pattern (mb:{address}, user:{address}:active, …)

Multi-domain

Multiple accepted domains with wildcard subdomain support

STARTTLS

Optional TLS upgrade via rustls

OpenTelemetry

OTLP traces with W3C traceparent propagation into relayed emails

Structured logging

JSON logs via tracing, configurable with RUST_LOG

Tiny footprint

Single static binary, ~5 MB, minimal memory

Async

Built on Tokio, handles thousands of concurrent connections

Docker-ready

Works as a sidecar in front of any backend mail server

Quick start

// Docker

docker run -d \
  --name burngate \
  -p 25:25 \
  -e ACCEPTED_DOMAINS=example.com \
  -e REDIS_HOST=your-redis-host \
  -e BACKEND_SMTP=your-backend:2525 \
  tempyemail/burngate:latest

Configuration

All configuration is via environment variables.

Variable Default Description
ACCEPTED_DOMAINS Required. Comma-separated list of accepted domains
BACKEND_SMTP 127.0.0.1:2525 Backend SMTP server to relay accepted mail to
REDIS_HOST 127.0.0.1 Redis hostname
REDIS_KEY_PATTERN mb:{address} Key pattern for mailbox lookup
REDIS_CHECK_MODE both key, set, or both
LISTEN_ADDR 0.0.0.0:25 Address and port to listen on
MAX_MESSAGE_SIZE 10485760 Maximum message size in bytes
CONNECTION_TIMEOUT 300 Connection timeout in seconds
TLS_CERT_PATH PEM certificate path for STARTTLS
TLS_KEY_PATH PEM private key path for STARTTLS
RUST_LOG info Log level: trace, debug, info, warn, error
OTEL_EXPORTER_OTLP_ENDPOINT OTLP gRPC endpoint for trace export. Unset = OTel disabled
OTEL_SERVICE_NAME burngate Service name in traces

OpenTelemetry tracing

Set OTEL_EXPORTER_OTLP_ENDPOINT to export traces via OTLP. Each SMTP session becomes a root span with the relay step as a child. A W3C traceparent header is injected into every relayed email so your downstream mail processor can attach its own spans to the same trace. Zero overhead when unset.

.NET Aspire dashboard

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:15901

OTEL_SERVICE_NAME=burngate

Any OTLP backend

OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317

OTEL_SERVICE_NAME=burngate

// Trace hierarchy

smtp.session (peer=1.2.3.4)

└── smtp.relay (sender=..., size=...)

[traceparent injected into email headers]

↓ downstream mail processor continues trace