DEV Community

Dhiraj Chatpar
Dhiraj Chatpar

Posted on

Firewall: only allow specific application server IPs

What Is an SMTP Relay Server?

An SMTP relay accepts outbound email from your applications and delivers it to the final destination mail servers (Gmail, Outlook, Yahoo, etc.). Unlike a local MTA that handles both inbound and outbound, a relay focuses purely on outbound delivery optimization.

Typical architecture:

Application → SMTP Relay → Internet → Recipient Mail Server
              (KumoMTA)   (TLS)
Enter fullscreen mode Exit fullscreen mode

The relay handles:

  • Message queuing and retry logic
  • TLS encryption with certificate management
  • Rate limiting and traffic shaping
  • DKIM signing and SPF passthrough
  • Bounce processing and delivery tracking

Self-Hosted vs Cloud SMTP Relay

Factor Self-Hosted (KumoMTA) Cloud SMTP (SendGrid, Mailgun)
Control Full (server, config, logs) Limited (API only)
Cost at 10M/month ~$1,500 (infra) ~$1,200 (paid plan)
Cost at 100M/month ~$5,000 (infra) ~$15,000+
Throughput ceiling Unlimited (scale infra) Shared, tier-limited
Compliance Full GDPR/CAN-SPAM control Shared responsibility
Setup complexity Medium Low
AI optimization Yes (KumoMTA native) Limited
Custom bounce processing Full Lua control API/webhook only

Choose self-hosted if: You send > 5M emails/month, have engineering capacity, need full compliance control, or want to eliminate per-email pricing.

Choose cloud SMTP if: You're under 1M emails/month, have no infra engineering, or need rapid setup without infrastructure management.


KumoMTA as SMTP Relay: Production Configuration

Core Relay Configuration

-- /etc/kumomta/relay.conf
-- Production SMTP relay configuration

-- SMTP Listener (accepts from internal apps)
kumo.start_smtp_listener {
    listen = "[::]:2525",  -- Internal relay port
    name = "relay-in",
    relay_hosts = { "10.0.0.0/8", "172.16.0.0/12" }, -- Internal networks only
    auth_require_tls = true, -- Require AUTH from application servers
}

-- HTTP API for application injection
kumo.start_http_listener {
    listen = "[::]:8080",
    trusted_hosts = { "10.0.0.0/8", "127.0.0.1" },
}

-- DKIM signing for all outbound
kumo.configure_dkim_signing {
    domain = "example.com",
    selector = "mail",
    key_file = "/etc/kumomta/keys/mail._domainkey.example.com.pem",
    headers = { "From", "To", "Subject", "Date", "Message-ID" },
}

-- TLS for outbound delivery
kumo.configure_tls {
    min_tls_version = "1.3",
    ciphers = "ECDHE-RSA-AES256-GCM-SHA384",
}

-- Prometheus metrics
kumo.start_http_listener {
    listen = "[::]:2000",
    trusted_hosts = { "127.0.0.1" },
}
Enter fullscreen mode Exit fullscreen mode

Application Integration via HTTP API

Applications inject mail via KumoMTA's HTTP API:

curl -X POST http://kumomta-relay:8080/v1/message \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "from": "orders@example.com",
    "to": ["customer@gmail.com", "customer2@yahoo.com"],
    "subject": "Your Order #12345 Has Shipped",
    "headers": {
      "X-Order-ID": "12345",
      "X-Campaign": "shipping-confirmation"
    },
    "html": "<h1>Your order is on its way!</h1>",
    "text": "Your order #12345 has shipped. Track at: https://track.example.com/12345"
  }'
Enter fullscreen mode Exit fullscreen mode

Multi-Tenant Relay Configuration

For SaaS platforms sending on behalf of customers:

-- Per-customer relay configuration
kumo.on("smtp_server_greeting", function(domain, meta)
    local tenant = meta.tenant

    if tenant then
        -- Apply per-tenant rate limits
        local limit = get_tenant_rate_limit(tenant)
        kumo.limit_sending(tenant, limit, { per = "minute" })

        -- Set tenant-specific DKIM
        local dkim = get_tenant_dkim(tenant)
        if dkim then
            kumo.sign_dkim(dkim.domain, dkim.selector, dkim.key_file)
        end
    end
end)
Enter fullscreen mode Exit fullscreen mode

Security: TLS, AUTH, and IP Allowlisting

TLS Configuration (Required for 2026)

-- Enforce TLS 1.3 for all delivery
kumo.configure_tls {
    min_tls_version = "1.3",
    prefer_server_ciphers = true,
}

-- For submission ports (SMTPS)
kumo.start_smtp_listener {
    listen = "[::]:465",
    name = "smtps",
    tls = {
        cert_file = "/etc/kumomta/tls/server.crt",
        key_file = "/etc/kumomta/tls/server.key",
    },
}
Enter fullscreen mode Exit fullscreen mode

Generate self-signed certificates for testing:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
  -subj "/CN=mail.example.com"
Enter fullscreen mode Exit fullscreen mode

SMTP AUTH Configuration

-- Require AUTH for submission port
kumo.start_smtp_listener {
    listen = "[::]:587",
    name = "submission",
    auth_require_tls = true,
    -- Supported mechanisms
    auth_mechanisms = { "PLAIN", "LOGIN", "CRAM-MD5" },
}
Enter fullscreen mode Exit fullscreen mode

Application authentication (for HTTP API):

-- API key validation
kumo.on("http_request", function(request)
    local token = request.headers["Authorization"]:gsub("Bearer ", "")
    if not validate_api_key(token) then
        return { status = 401, body = "Unauthorized" }
    end
end)
Enter fullscreen mode Exit fullscreen mode

IP Allowlisting

-- Only accept connections from trusted IPs
kumo.start_smtp_listener {
    listen = "[::]:2525",
    name = "relay-in",
    relay_hosts = { "10.0.0.0/8", "172.16.0.0/12" },
    -- Deny all other IPs
}
Enter fullscreen mode Exit fullscreen mode

For additional security with cloud applications:

# Firewall: only allow specific application server IPs
sudo ufw allow from 10.0.1.0/24 to any port 2525
sudo ufw allow from 10.0.2.0/24 to any port 2525
Enter fullscreen mode Exit fullscreen mode

Performance Tuning for High-Volume Relay

Connection and Thread Tuning

-- /etc/kumomta/tuning.conf

-- Delivery threads (scale with CPU cores)
kumo.configure_delivery {
    threads = 16, -- 2x CPU cores
    max_connections_per_domain = 10,
    max_message_rate = 1000, -- per minute per domain
}

-- Queue management
kumo.configure_queue {
    max_queue_depth = 100000,
    retry_interval = "5m",
    max_retry_age = "72h",
    dead_letter_after = "7d",
}
Enter fullscreen mode Exit fullscreen mode

Monitoring Metrics

Key metrics to track:

Metric Target Alert
kumomta_queue_depth < 10,000 > 50,000
kumomta_delivery_latency_p95 < 30s > 60s
kumomta_smtp_errors_total < 0.1% > 1%
kumomta_tls_failures_total < 0.5% > 2%
Connections per domain < 10 > 20

SMTP Relay for Specific Use Cases

Transactional Email (Order Confirmations, Password Resets)

Requirements:

  • Immediate delivery (< 30 seconds end-to-end)
  • High reliability (99.9% success rate)
  • DKIM signing required
  • Low volume per user, high total volume
-- Transactional: low latency priority
kumo.on("smtp_message_received", function(domain, meta)
    local headers = meta.headers or {}
    if headers["X-Type"] == "transactional" then
        -- Elevate priority
        kumo.set_queue_priority("high")
    end
end)
Enter fullscreen mode Exit fullscreen mode

Bulk/Marketing Email

Requirements:

  • High throughput (100K+ emails/hour)
  • Per-campaign tracking
  • Bounce processing integration
  • List unsubscribe handling (RFC 8058)
-- Bulk: throughput priority
kumo.on("smtp_message_received", function(domain, meta)
    local headers = meta.headers or {}
    local campaign = headers["X-Campaign"]

    if campaign then
        -- Track campaign metrics
        increment_campaign_metric(campaign, "queued")
    end
end)

-- List-Unsubscribe header (RFC 8058 - required for bulk)
kumo.on("smtp_message_received", function(domain, meta)
    if is_bulk_campaign(meta.headers) then
        kumo.add_header("List-Unsubscribe", "<mailto:unsubscribe@example.com?subject=unsubscribe>")
        kumo.add_header("List-Unsubscribe-Post", "List-Unsubscribe=One-Click")
    end
end)
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Common SMTP Relay Issues

Issue: Connection Timeout / Slow Delivery

Cause: Network latency, remote MX throttling, or TLS negotiation failure
Fix:

# Test SMTP connectivity
telnet gmail-smtp-in.l.google.com 25

# Check TLS
openssl s_client -connect gmail-smtp-in.l.google.com:25 -starttls smtp

# Check KumoMTA queue
curl http://localhost:2000/metrics | grep queue_depth
Enter fullscreen mode Exit fullscreen mode

Issue: 421/429 Throttling from Gmail/MS

Cause: ISP is throttling your IP — you're sending too fast
Fix: Implement adaptive throttling in your relay policy:

kumo.on("smtp_delivery_result", function(result, meta)
    if result.code == 421 or result.code == 429 then
        -- Reduce rate by 30%
        adjust_domain_rate(meta.domain, -0.3)
        log_warn("Throttled by " .. meta.domain .. ", reducing rate")
    end
end)
Enter fullscreen mode Exit fullscreen mode

Issue: TLS Handshake Failures

Cause: Outdated TLS config or untrusted certificate
Fix: Ensure min_tls_version = "1.2" minimum (1.3 preferred). Check certificate expiration:

openssl x509 -in /etc/kumomta/tls/server.crt -noout -dates
Enter fullscreen mode Exit fullscreen mode

FAQ

Q: What's the difference between SMTP port 25, 465, and 587?
A: Port 25 is the standard MX port (server-to-server delivery). Port 465 (SMTPS) is for legacy submission with implicit TLS. Port 587 (Submission) is the modern standard for application-to-relay submission with explicit STARTTLS. Use 587 for your application relay integration.

Q: Can KumoMTA relay from Postfix?
A: Yes. Configure Postfix to relay to KumoMTA on an internal port:

relayhost = [kumomta.internal]:2525
Enter fullscreen mode Exit fullscreen mode

Q: How do I handle SMTP relay for WordPress/WooCommerce?
A: Install WP Mail SMTP plugin, configure it to point to your KumoMTA relay at kumomta.example.com:587 with AUTH credentials. KumoMTA will DKIM-sign and deliver all WordPress mail.

Q: Is port 25 blocked by cloud providers — what do I do?
A: Most cloud providers (AWS, GCP, Azure) block port 25 by default. Solutions: (1) Use port 587 submission instead, (2) request port 25 unblock from your provider, (3) use a dedicated SMTP relay service for the "last mile" if needed.

Q: How does KumoMTA compare to Postfix for relay use?
A: KumoMTA has superior multi-tenant traffic shaping, built-in DKIM, and Prometheus metrics. Postfix requires external tools (OpenDKIM, policyd) for equivalent functionality. For high-volume relay, KumoMTA is significantly easier to operate.


Get Help With SMTP Relay Setup

PostMTA provides:

  • Production KumoMTA relay configuration and deployment
  • Secure TLS and AUTH setup
  • Multi-tenant relay for SaaS platforms
  • Integration with existing applications (WordPress, Shopify, custom)
  • 24/7 relay monitoring and alerting

👉 Get SMTP relay consulting →

For related guides, see KumoMTA Setup Guide, Email Authentication Guide, and Open Source Email Infrastructure.

References: RFC 5321 (SMTP) | RFC 8460 (SMTP TLS Reporting)

Top comments (0)