Skip to content

Repository files navigation

Sendry

Self-hosted email delivery infrastructure

Fast. Reliable. Yours.

Your App → Sendry → Recipient
(SMTP/API)

Русская версия

What is Sendry?

A complete email sending solution you can run on your own servers:

  • MTA Server — SMTP relay with queue, retries, DKIM, TLS
  • HTTP API — REST interface for sending and tracking emails
  • Web Panel — Manage templates, campaigns, and recipients

Key Features

  • Secure — Let's Encrypt, DKIM signing, auth protection
  • Observable — Prometheus metrics, detailed logging
  • Scalable — Persistent queue, concurrent workers
  • Flexible — Multi-domain, rate limiting, sandbox mode
  • Manageable — Web UI for everything

Who Uses Sendry?

  • Teams sending transactional emails (confirmations, alerts, reports)
  • Marketers running email campaigns without per-message costs
  • Developers who need a local SMTP server for testing
  • Organizations requiring email archiving and compliance

Features

  • SMTP server (ports 25, 587) with AUTH support
  • SMTPS server (port 465) with implicit TLS
  • STARTTLS support for secure connections
  • Let's Encrypt (ACME) automatic certificate management
  • DKIM signing for outgoing emails
  • HTTP API for sending emails
  • Persistent queue with BoltDB
  • Retry logic with exponential backoff
  • Multi-domain support with different modes:
    • production - normal delivery
    • sandbox - capture emails locally (for testing)
    • redirect - redirect all emails to specified addresses
    • bcc - normal delivery + copy to archive
  • Rate limiting (per domain, sender, IP, API key)
  • Prometheus metrics with persistence
  • Bounce handling
  • Graceful shutdown
  • Structured JSON logging

Installation

Package Repository (recommended)

# Debian/Ubuntuecho"deb [trusted=yes] https://foxzi.github.io/sendry/apt stable main"| sudo tee /etc/apt/sources.list.d/sendry.list
sudo apt update && sudo apt install sendry
# RHEL/CentOS/Fedora
sudo tee /etc/yum.repos.d/sendry.repo << 'EOF'[sendry]name=Sendry Repositorybaseurl=https://foxzi.github.io/sendry/rpm/stable/$basearch/enabled=1gpgcheck=0EOF
sudo dnf install sendry
# Alpine
wget https://github.com/foxzi/sendry/releases/latest/download/sendry_0.4.18-r1_x86_64.apk
apk add --allow-untrusted sendry_*.apk

From Binary

wget https://github.com/foxzi/sendry/releases/latest/download/sendry-linux-amd64
chmod +x sendry-linux-amd64
sudo mv sendry-linux-amd64 /usr/local/bin/sendry

Docker

docker pull ghcr.io/foxzi/sendry:latest
docker run -p 25:25 -p 587:587 -p 8080:8080 \
-v /path/to/config.yaml:/etc/sendry/config.yaml \
ghcr.io/foxzi/sendry:latest

Docker Compose

For running both Sendry MTA and Web management panel, see Docker documentation.

git clone https://github.com/foxzi/sendry.git
cd sendry
cp configs/sendry.example.yaml configs/sendry.yaml
cp configs/web.example.yaml configs/web.yaml
# Edit configs
docker compose up -d

Ansible

For automated deployment on multiple servers, see Ansible documentation.

cd ansible
cp inventory/hosts.yml.example inventory/hosts.yml
# Edit hosts.yml with your servers
ansible-playbook -i inventory/hosts.yml playbooks/sendry.yml

Build from Source

Requires Go 1.24+

git clone https://github.com/foxzi/sendry.git
cd sendry
make build

Quick Start

For detailed quick start guide see docs/quickstart.md.

Configuration

Copy and edit the example configuration:

cp configs/sendry.example.yaml config.yaml

Minimal configuration:

server:
hostname: "mail.example.com"smtp:
domain: "example.com"auth:
required: trueusers:
myuser: "mypassword"api:
api_key: "your-secret-api-key"storage:
path: "./data/queue.db"

Run

./sendry serve -c config.yaml

Validate Configuration

./sendry config validate -c config.yaml

API

Health Check

curl http://localhost:8080/health

Response:

{
"status": "ok",
"version": "0.4.18",
"uptime": "1h30m",
"queue": {
"pending": 5,
"sending": 1,
"delivered": 100,
"failed": 2,
"deferred": 3,
"total": 111
}
}

Send Email

curl -X POST http://localhost:8080/api/v1/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "from": "sender@example.com", "to": ["recipient@example.com"], "subject": "Test", "body": "Hello, World!", "html": "<p>Hello, <b>World</b>!</p>" }'

Response:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}

Check Status

curl http://localhost:8080/api/v1/status/{message_id} \
-H "Authorization: Bearer YOUR_API_KEY"

Response:

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "delivered",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:05Z",
"retry_count": 0
}

Queue Stats

curl http://localhost:8080/api/v1/queue \
-H "Authorization: Bearer YOUR_API_KEY"

Delete Message

curl -X DELETE http://localhost:8080/api/v1/queue/{message_id} \
-H "Authorization: Bearer YOUR_API_KEY"

Configuration Reference

ParameterDefaultDescription
server.hostnameOS hostnameServer FQDN
smtp.listen_addr:25SMTP relay port
smtp.submission_addr:587SMTP submission port
smtp.smtps_addr:465SMTPS port (implicit TLS)
smtp.domainrequiredMail domain
smtp.max_message_bytes10485760Max message size (10MB)
smtp.max_recipients100Max recipients per message
smtp.auth.requiredfalseRequire authentication
smtp.auth.users{}Username -> password map
smtp.auth.max_failures5Max auth failures before blocking
smtp.auth.block_duration15mHow long to block after max failures
smtp.auth.failure_window5mWindow for counting failures
smtp.tls.cert_file""TLS certificate file path
smtp.tls.key_file""TLS private key file path
smtp.tls.acme.enabledfalseEnable Let's Encrypt
smtp.tls.acme.email""ACME account email
smtp.tls.acme.domains[]Domains for certificate
smtp.tls.acme.cache_dir/var/lib/sendry/certsCertificate cache
dkim.enabledfalseEnable DKIM signing
dkim.selector""DKIM selector
dkim.domain""DKIM domain
dkim.key_file""DKIM private key path
api.listen_addr:8080HTTP API port
api.api_key""API key (empty = no auth)
api.max_header_bytes1048576Max HTTP header size (1MB)
api.read_timeout30sHTTP read timeout
api.write_timeout30sHTTP write timeout
api.idle_timeout60sHTTP idle timeout
queue.workers4Number of delivery workers
queue.retry_interval5mBase retry interval
queue.max_retries5Max delivery attempts
storage.path/var/lib/sendry/queue.dbBoltDB file path
storage.retention.delivered_max_age0Delete delivered messages older than this
storage.retention.cleanup_interval1hCleanup interval
dlq.enabledtrueEnable dead letter queue
dlq.max_age0Delete DLQ messages older than this
dlq.max_count0Max DLQ messages (0 = unlimited)
dlq.cleanup_interval1hDLQ cleanup interval
logging.levelinfoLog level (debug/info/warn/error)
logging.formatjsonLog format (json/text)
metrics.enabledfalseEnable Prometheus metrics
metrics.listen_addr:9090Metrics server port
metrics.path/metricsMetrics endpoint path
metrics.flush_interval10sCounter persistence interval
metrics.allowed_ips[]IPs/CIDRs allowed to access metrics

See documentation:

Project Structure

sendry/
├── cmd/sendry/ # CLI entry point
├── internal/
│ ├── api/ # HTTP API server
│ ├── app/ # Application orchestration
│ ├── config/ # Configuration
│ ├── dkim/ # DKIM signing
│ ├── dns/ # MX resolver
│ ├── metrics/ # Prometheus metrics
│ ├── queue/ # Message queue & storage
│ ├── smtp/ # SMTP server & client
│ └── tls/ # TLS/ACME support
├── configs/ # Example configurations
└── docs/ # Documentation

License

GPL-3.0

About

Sendry MTA

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages