Blog
KumoMTA: Why I'm Enjoying It More Than PMTA (and the automation I'm building next)
KumoMTA: Why I'm Enjoying It More Than PMTA (and the automation I'm building next)🔗
I've spent a lot of the last year elbows-deep in MTAs. I've run and tuned Postfix, I've spent plenty of time with PowerMTA (PMTA), and lately I've been living in KumoMTA. Short version: I'm liking KumoMTA better than PMTA for the way I work—fewer walls, more composability, and a modern, observable core that doesn't fight me when I want to automate or scale.
This isn't a "PMTA bad" post. PMTA has shipped billions of emails for people and has the battle scars to prove it. But if you're hands-on, prefer code to wizards, and want to treat email infrastructure like software (with repos, CI, metrics, and repeatable deploys), KumoMTA feels like home.
What's clicking for me🔗
1) Modern, code-first ergonomics🔗
KumoMTA's configuration and policy hooks lend themselves to version control and repeatability. I keep my routing, pools, throttles, and policy in a repo with per-env overlays—dev, staging, prod. I can review diffs, attach comments, and roll forward/back just like the rest of my stack. PMTA can be automated, but it always felt like I was scripting around a product; with KumoMTA I'm developing inside a platform.
2) Observability that's actually useful🔗
I care about shaping, queue depth, defer reasons, retry patterns, and delivery latencies by domain. KumoMTA exposes the right data to wire up real dashboards and alerts quickly (think: per-domain throughput, failure codes, retry backoff, TLS stats). When something blips—DNS hiccup, rate limit, an ISP's mood swing—I can see it and react without grepping logs in the dark.
3) Policy hooks without contortions🔗
Warmup rules, domain-specific throttles, and "only escalate if reputation recovers" logic is straightforward. The control I need—per-IP/per-domain/per-recipient shaping, connection caps, concurrency, retry windows—maps cleanly to the primitives KumoMTA gives me. That reduces the amount of "glue code" I have to carry.
4) Performance that doesn't demand heroics🔗
Throughput is strong without turning the box into a science project. It scales predictably across CPU cores and plays nicely with containerized deployments. I can run lean when I'm testing and open the taps when I need to move volume.
5) Cost and control🔗
No license drama, no appliance mindset. I can run multiple environments, experiment, and tear down without paperwork. For an engineering-heavy workflow, that freedom matters.
Day-to-day: what my workflow looks like🔗
- Everything in Git. KumoMTA configs, policy modules, domain templates, and dashboards live in the repo. Feature branches for new warmup schedules, rate limit profiles, and ISP quirks.
- CI checks. Lint configs, validate domain maps, smoke-test a "synthetic deliverability" path in dev, and gate merges behind a quick send/receive loop.
- Deploy = push + reload. A tagged release bakes a container, ships to the cluster, and does a zero-drama reload. If I mess something up, rollback is just a previous tag.
Where KumoMTA beats PMTA for me🔗
- I can treat the MTA like code—policy, routing, throttles—rather than a black box I poke via a control panel.
- Metrics-first operations fit how I debug: I want to observe, not guess.
- Easier automation surface area. I don't need to bend around someone else's scripting model; I define my own control plane and KumoMTA cooperates.
Again, PMTA can run massive programs and it's proven. But for my style—fast iteration, lots of custom policy, CI-driven changes—KumoMTA simply fits better.
WIP: one-click domain lifecycle (add/update/delete)🔗
The next step I'm building is full domain lifecycle automation—so I (or a teammate) can add or retire domains safely with a single API call. It's in progress, and here's the shape:
Goals🔗
- Idempotent: running "add this domain" twice is harmless.
- Atomic: DNS, DKIM, MTA config, and warmup profile land together—or we roll back.
- Auditable: every change is recorded (who, when, what) with diffs.
- Safe by default: new domains start in a conservative warmup schedule until reputation improves.
Flow (Add Domain)🔗
-
Reserve domain in the control DB (status:
pending_setup). -
Generate DKIM keys and write public TXT → produce a DNS change set (SPF, DKIM, DMARC, tracking).
-
Render KumoMTA fragments from templates:
- IP pool / binding (per-tenant, per-region)
- Per-domain routing + throttle profile
- Bounce/feedback classification rules
-
Apply DNS via provider API (Cloudflare/Route53/…); poll until verified.
-
Load config + reload KumoMTA (dry-run validation first).
-
Assign warmup schedule (start tiny: connections, concurrency, daily cap).
-
Flip status →
activeand emit a webhook/Slack/Telegram notice with links to the domain's dashboards.
Flow (Delete/Retire Domain)🔗
- Drain queues and pause new injects for the domain.
- Remove routes/pools and reload KumoMTA.
- Optionally remove DNS (or just disable DKIM and DMARC; leave SPF neutral).
- Archive metrics and set status →
retired.
Minimal control-plane API (sketch)🔗
POST /domains { "domain": "example.com", "tenant_id": 42 }
DELETE /domains/example.com
GET /domains/example.com/status
POST /domains/example.com/warmup { "profile": "conservative-30d" }
Template-driven render (sketch)🔗
# templates/domain_route.j2
domain "{{ domain }}" {
pool = "{{ pool_name }}";
max_connections = {{ warmup.max_conn }};
max_concurrency = {{ warmup.max_conc }};
rate_per_minute = {{ warmup.rpm }};
retry_backoff = "{{ warmup.backoff }}";
}
Rendered fragments drop into a conf.d/domains/ directory; CI validates, and a controller issues a safe reload.
Guardrails I'm adding🔗
- Preflight checks: MX conflicts, DNS ownership, SPF length, DMARC policy sanity.
- Synthetic tests: automatic loop test (send → receive → classify) before marking
active. - Gradual ramp: scheduled increases (daily) tied to reputation/soft-bounce signals.
- Kill switch: one click to pause a domain if bounces spike.
What's next🔗
- Finish the CRUD flows and publish a tiny CLI so ops can run
mta domains add example.comand get the same, safe outcome as the API. - Ship Grafana dashboards that are domain-centric (warmup day, RPM, defers by code, TLS success rate, complaint/bounce rates).
- Write migration scripts to import existing domains and backfill DKIM + policy from our current configs.
If you're running PMTA today and you enjoy coding more than clicking, KumoMTA is worth a serious look. It lines up with a software-defined way of operating mail at scale—and it's making my day-to-day simpler, faster, and a lot more fun.
PS: I'm actively building the domain automation above. If you're interested in the approach or want to compare notes, I'll share more once the WIP lands in prod.
AI-assisted writing
I draft and edit all articles myself, and I use AI as an assistant for outlining, phrasing, and cleanup. Curious how I use it—and where I draw the lines?