Kannon
✉️ Cloud-Native massive mail sender for Kubernetes!
Install / Use
/learn @kannon-email/KannonREADME
Kannon 💥

A Cloud Native SMTP mail sender for Kubernetes and modern infrastructure.
[!NOTE] Due to limitations of AWS, GCP, etc. on port 25, this project will not work on cloud providers that block port 25.
Table of Contents
- Features
- Architecture
- Quickstart
- Configuration
- Database Schema
- API Overview
- Deployment
- Domain & DNS Setup
- Sending Mail
- Testing & Demo Mode
- Development & Contributing
- License
Features
- Cloud-native, scalable SMTP mail sending
- gRPC API for sending HTML and templated emails
- DKIM and SPF support for deliverability
- Attachments support (JSONB in DB)
- Custom fields for emails (JSONB in DB)
- Statistics and analytics (DB + gRPC API)
- Template management (CRUD via API)
- Kubernetes-ready deployment
- Postgres-backed persistence
Planned:
- Multi-node sending
- Advanced analytics dashboard
Architecture
Kannon is composed of several microservices and workers:
- API: gRPC server for mail, template, and domain management
- SMTP: Handles SMTP protocol and relays mail
- Sender: Sends emails from the queue
- Dispatcher: Manages the sending pool and delivery
- Verifier: Validates emails before sending
- Bounce: Handles bounces
- Stats: Collects and stores delivery statistics
All components can be enabled/disabled via CLI flags or config.
See
ARCHITECTURE.mdfor a full breakdown of modules, NATS streams, topics, consumers, and message flows.
flowchart TD
subgraph Core
API["gRPC API"]
SMTP["SMTP Server"]
Sender["Sender"]
Dispatcher["Dispatcher"]
Verifier["Verifier"]
Bounce["Bounce"]
Stats["Stats"]
end
DB[(PostgreSQL)]
NATS[(NATS)]
API <--> DB
Dispatcher <--> DB
Sender <--> DB
Verifier <--> DB
Stats <--> DB
Bounce <--> DB
API <--> NATS
Sender <--> NATS
Dispatcher <--> NATS
SMTP <--> NATS
Stats <--> NATS
Verifier <--> NATS
Bounce <--> NATS
Quickstart
Prerequisites
- Go 1.25.5+
- Docker (optional, for containerized deployment)
- PostgreSQL database
- NATS server (optional - embedded mode available in standalone command)
Standalone Mode (Recommended for Development/Testing)
Run all Kannon components in a single process with embedded NATS (only PostgreSQL required):
git clone https://github.com/kannon-email/kannon.git
cd kannon
go build -o kannon .
./kannon standalone --config ./config.yaml
This mode:
- Runs all components (API, SMTP, Sender, Dispatcher, Verifier, Stats, Bounce)
- Embeds NATS server (no external NATS required)
- Ideal for development, testing, or single-server deployments
- Still requires a PostgreSQL database
Local Run (Manual Component Selection)
git clone https://github.com/kannon-email/kannon.git
cd kannon
go build -o kannon .
./kannon --run-api --run-smtp --run-sender --run-dispatcher --config ./config.yaml
Note: This mode requires an external NATS server configured in your config file.
Docker Compose
See examples/docker-compose/ for ready-to-use files.
docker-compose -f examples/docker-compose/docker-compose.yaml up
- Edit
examples/docker-compose/kannon.yamlto configure your environment.
Makefile Targets
make test— Run all testsmake generate— Generate DB and proto codemake lint— Run linters
Configuration
Kannon can be configured via YAML file, environment variables, or CLI flags. Precedence: CLI > Env > YAML.
Main config options:
| Key / Env Var | Type | Default | Description |
| ----------------------------------------------- | -------- | -------------- | -------------------------------------- |
| database_url / K_DATABASE_URL | string | (required) | PostgreSQL connection string |
| nats_url / K_NATS_URL | string | (required) | NATS server URL for internal messaging |
| debug / K_DEBUG | bool | false | Enable debug logging |
| api.port / K_API_PORT | int | 50051 | gRPC API port |
| sender.hostname / K_SENDER_HOSTNAME | string | (required) | Hostname for outgoing mail |
| sender.max_jobs / K_SENDER_MAX_JOBS | int | 10 | Max parallel sending jobs |
| sender.demo_sender / K_SENDER_DEMO_SENDER | bool | false | Enable demo sender mode for testing |
| smtp.address / K_SMTP_ADDRESS | string | :25 | SMTP server listen address |
| smtp.domain / K_SMTP_DOMAIN | string | localhost | SMTP server domain |
| smtp.read_timeout / K_SMTP_READ_TIMEOUT | duration | 10s | SMTP read timeout |
| smtp.write_timeout / K_SMTP_WRITE_TIMEOUT | duration | 10s | SMTP write timeout |
| smtp.max_payload / K_SMTP_MAX_PAYLOAD | size | 1024kb | Max SMTP message size |
| smtp.max_recipients / K_SMTP_MAX_RECIPIENTS | int | 50 | Max recipients per SMTP message |
| run-api / K_RUN_API | bool | false | Enable API server |
| run-smtp / K_RUN_SMTP | bool | false | Enable SMTP server |
| run-sender / K_RUN_SENDER | bool | false | Enable sender worker |
| run-dispatcher / K_RUN_DISPATCHER | bool | false | Enable dispatcher worker |
| run-verifier / K_RUN_VERIFIER | bool | false | Enable verifier worker |
| run-bounce / K_RUN_BOUNCE | bool | false | Enable bounce worker |
| run-stats / K_RUN_STATS | bool | false | Enable stats worker |
| config | string | ~/.kannon.yaml | Path to config file |
- See
examples/docker-compose/kannon.yamlfor a full example.
Database Schema
Kannon requires a PostgreSQL database. Main tables:
- domains: Registered sender domains, DKIM keys
- api_keys: API keys for authentication (multiple keys per domain, expirable, revocable)
- messages: Outgoing messages, subject, sender, template, attachments, custom headers
- sending_pool_emails: Email queue, status, scheduling, custom fields
- templates: Email templates, type, metadata
- stats: Delivery and open/click statistics
- stats_keys: Public/private keys for stats security
See db/migrations/ for full schema and migrations.
API Overview
Kannon exposes a gRPC API for sending mail, managing domains/templates, and retrieving stats.
Services & Methods
- Mailer API (proto)
SendHTML: Send a raw HTML emailSendTemplate: Send an email using a stored template
- Admin API (proto)
- Domains:
GetDomains,GetDomain,CreateDomain - Templates:
CreateTemplate,UpdateTemplate,DeleteTemplate,GetTemplate,GetTemplates - API Keys:
CreateAPIKey,ListAPIKeys,GetAPIKey,DeactivateAPIKey
- Domains:
- Stats API (proto)
GetStats,GetStatsAggregated
Authentication
All gRPC APIs use Basic Auth with your domain and API key:
token = base64(<your domain>:<your api key>)
Pass this in the Authorization metadata for gRPC calls:
{
"Authorization": "Basic <your token>"
}
Example: SendHTML Request
{
"sender": {
"email": "no-reply@yourdomain.com",
"alias": "Your Name"
},
"recipients": ["user@example.com"],
"subject": "Test",
"html": "<h1>Hello</h1><p>This is a test.</p>",
"attachments": [
{ "filename": "file.txt", "content": "base64-encoded-content" }
],
"fields": { "custom": "value" },
"headers": {
"to": ["visible-recipient@example.com"],
"cc": ["cc@example.com"]
}
}
Headers
The optional headers field allows overriding the To and adding a Cc header on sent emails. The SMTP envelope recipient (actual delivery target) remains the pool recipient, but the visible mail headers will use the values from headers:
to: Overrides theToheader displayed in the email clientcc: Adds aCcheader to the email
This is useful for scenarios where you want the email to appear addressed to a group or alias while delivering to individual recipients.
See the proto files for all fields and options.
Deployment
Kubernetes
- See
k8s/deployment.yamlfor a production-ready manifest. - Configure your environment via a mounted YAML file or environment variables.
Docker Compose
- See
examples/docker-compose/for local or test deployments.
Domain & DNS Setup
To send mail, you must register a sender domain and configure DNS:
- Register a domain via the Admin API
Related Skills
node-connect
351.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
110.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
351.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
351.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
