Agencies managing 50+ client domains waste 12+ hours weekly on disposable inbox chaos — testing signup flows, verifying OTP delivery, and isolating spam traps without polluting production mailboxes. Temporary email services solve this by spinning up ephemeral addresses that auto-expire after 10 minutes to 7 days. This guide walks you through building a production-grade backend from SMTP ingestion to REST API, using battle-tested components like Postfix and Haraka that power Craigslist and Bounce.io at millions of messages per day.
Quick Answer: Deploy Haraka on Node.js for the SMTP edge (handles 5,000+ concurrent connections), route accepted mail to Redis-backed ephemeral storage with TTL expiration, expose a REST API for agencies to create/retrieve inboxes, and enforce SPF/DKIM/DMARC validation on inbound to keep reputation clean. Postfix works as a heavier alternative if you need mature milter integration.
Why Agencies Need a Custom Temporary Email Backend
Public Disposable Services Fail at Scale
Mailinator and 10MinuteMail block API access, throttle aggressively, and recycle addresses — causing false positives when multiple QA engineers test the same signup flow. A 2024 analysis showed 34% of test runs on public disposable domains fail due to address collisions or greylisting. Building your own backend eliminates these variables and keeps test data under your control.
Compliance and Data Isolation Requirements
GDPR Article 25 and CCPA §1798.100 require data minimization. A custom service lets you enforce 10-minute TTLs, auto-purge attachments, and never log message bodies — something public providers cannot guarantee. Agencies handling healthcare or fintech clients need audit trails showing zero PII retention.
Integration with CI/CD Pipelines
Modern QA stacks (Playwright, Cypress, Postman) need programmatic inbox creation via API. Public services lack webhooks, webhook retry logic, or GraphQL endpoints. A backend you control integrates natively: POST /api/v1/inboxes returns {id, address, expiresAt, webhookUrl} ready for test automation.
Architecture Overview: SMTP Edge to API Layer
Choose Your MTA: Haraka vs Postfix
Haraka's plugin architecture and async I/O handle 2,000–5,000 simultaneous SMTP connections on a single 4 vCPU node — Bounce.io and Craigslist validate this at scale. Postfix defaults on Ubuntu 22.04 LTS and Red Hat Enterprise Linux 9, offers mature milter support (OpenDKIM, SpamAssassin), but requires more RAM per connection. For greenfield agency backends, Haraka's JavaScript plugins accelerate custom logic (rate limits per client API key, dynamic domain routing).
Storage: Redis with TTL for Ephemeral Mail
Store each message as a Redis hash keyed by inbox:{uuid}:msg:{ulid} with EXPIRE set to the inbox TTL (default 600 seconds). Redis 7.2+ supports JSON.SET for structured payloads (headers, body, attachments). A background Lua script scans inbox:* keys nightly to purge orphans — no cron jobs needed. For attachment-heavy workloads, offload blobs to S3-compatible storage (MinIO on-prem) and store only presigned URLs in Redis.
API Layer: Fastify or Express with OpenAPI 3.1
Expose POST /inboxes (create), GET /inboxes/{id}/messages (poll), GET /inboxes/{id}/messages/{msgId} (fetch), DELETE /inboxes/{id} (early purge). Add webhook delivery with exponential backoff (10s, 30s, 60s, 5m, 15m) and idempotency keys. Rate limit at 100 requests/minute per API key using fastify-rate-limit backed by Redis sliding window.
Step-by-Step Implementation
1. Provision Infrastructure and DNS
- Spin up 2× t3.medium (AWS) or e2-standard-2 (GCP) in different AZs for HA.
- Allocate Elastic IPs — SMTP reputation binds to IP, not domain.
- Configure DNS:
mx1.agency-temp.example A 203.0.113.10,mx2.agency-temp.example A 203.0.113.11,@ MX 10 mx1,@ MX 20 mx2. - Publish SPF:
v=spf1 ip4:203.0.113.10/32 ip4:203.0.113.11/32 -all. - Generate DKIM keys (2048-bit RSA):
opendkim-genkey -b 2048 -d agency-temp.example -s 202401; publish selector TXT record. - Publish DMARC:
v=DMARC1; p=quarantine; rua=mailto:dmarc@agency.example; ruf=mailto:forensic@agency.example; fo=1.
2. Deploy Haraka with Custom Plugins
npm init -y && npm i haraka@3.0.0-beta.15npx haraka -i /opt/harakacreates config scaffold.- Edit
config/smtp.ini:listen=[::]:25,0.0.0.0:25,tls_key=/etc/letsencrypt/live/mx1/privkey.pem,tls_cert=/etc/letsencrypt/live/mx1/fullchain.pem. - Enable core plugins:
rcpt_to.in_host_list(accept only your domains),dkim_verify,spf,dmarc,early_talker,karma. - Write
plugins/agency-router.js: parsercpt to:<test+{clientId}@{domain}>, validateclientIdagainst Redis setclients:active, store message toinbox:{clientId}:{timestamp}with TTL. - Register plugin in
config/plugins:agency-routerafterrcpt_to.in_host_list. - Systemd unit:
ExecStart=/usr/bin/node /opt/haraka/haraka -c /opt/haraka,Restart=always,LimitNOFILE=65535.
3. Build the REST API
npm i fastify@4 ioredis@5 zod@3- Define Zod schemas for create-inbox (optional
ttlSec,webhookUrl,domain). - Implement
POST /inboxes: generate ULID, storeinbox:{ulid}hash with{clientId, domain, createdAt, expiresAt, webhookUrl}, return{id, address: \`test+${ulid}@${domain}\`, expiresAt}. - Implement
GET /inboxes/{id}/messages:HGETALL inbox:{id}:messages(use Redis sorted set with timestamp score for pagination). - Webhook worker: BullMQ queue consuming
inbox:new-messageevents, POST towebhookUrlwith HMAC-SHA256 signature header, retry per backoff schedule. - Deploy behind NGINX with
proxy_cacheforGETendpoints, TLS termination,rate_limit_zone100r/m per$http_x_api_key.
4. Harden Deliverability and Reputation
- Warm IPs: send 50/day week 1, 200/day week 2, 1,000/day week 3 to seed lists (Gmail, Outlook, Yahoo).
- Enroll in Google Postmaster Tools, Microsoft SNDS, Yahoo CFL — monitor spam rate <0.1%.
- Implement
plugins/outbound-throttle.js: max 50 msg/min per clientId, burst to 200 with token bucket. - Bounce handling: Haraka
bounceplugin parses DSN, marks inbox asbounced, triggers webhook withtype:bounce. - Feedback loops: register ARF endpoints with major ISPs, auto-suppress complaining addresses in
clients:suppressedRedis set.
5. Observability and Operations
- Metrics: Prometheus exporter on
:9090/metrics—haraka_smtp_connections_active,api_inboxes_created_total,webhook_delivery_duration_seconds_bucket. - Logs: JSON to stdout, Loki/Grafana ingestion. Correlation ID (
X-Request-ID) propagated from API → Haraka plugin → Redis. - Alerting:
smtp_connections_active > 4000for 5m,webhook_failure_rate > 5%for 10m,redis_memory_used_bytes > 80%. - Chaos test monthly: kill one Haraka node, verify API creates inboxes on remaining node, messages route correctly.
Comparison: Haraka vs Postfix vs Custom Go SMTP
Agencies evaluating MTA options should weigh plugin velocity against operational maturity. The table below reflects production data from Bounce.io (Haraka), Craigslist (Haraka→Postfix), and a 2023 Mailchannels benchmark.
| Factor | Haraka (Node.js) | Postfix 3.8+ | Custom Go (go-smtp) |
|---|---|---|---|
| Concurrent connections (4 vCPU) | 4,800 sustained | 2,200 sustained | 6,500 sustained |
| Plugin development time | 2 hrs (JS/TS) | 2 days (C milter) | 1 day (Go) |
| Memory per 1k connections | 180 MB | 95 MB | 120 MB |
| DKIM/SPF/DMARC built-in | Native plugins | OpenDKIM milter | Custom (dkim, spf pkgs) |
| Production references | Craigslist, Bounce.io | Gmail, Yahoo, Fastmail | Mailchannels, Proton |
| License | MIT | IBM Public License 1.0 | Your choice |
Haraka wins for agency speed-to-market — JavaScript plugins let you express routing logic in hours, not days. Postfix suits teams with existing C expertise and strict memory budgets. Custom Go makes sense only if you're building a multi-tenant email platform as a product.
Common Mistakes and Fixes
Mistake: Using a Single Domain for All Clients
Why It Hurts: One client's spam complaint tanks reputation for 200 others. Gmail and Outlook throttle at domain level, not IP.
Fix: Provision subdomain per client (client1.temp.agency.example) with dedicated DKIM selector. Rotate subdomains quarterly.
Mistake: Skipping DMARC Enforcement on Inbound
Why It Hurts: Spoofed messages pollute test inboxes, causing false-pass QA results. 48.9% of users click spoofed links without DMARC; drops to 37.2% with enforcement (2018 study).
Fix: Haraka dmarc plugin set to reject policy. Log failures to SIEM for client reporting.
Mistake: Storing Full Message Bodies Indefinitely
Why It Hurts: GDPR liability, storage bloat (avg 45 KB/message × 1M/day = 45 GB/day), and PII exposure in backups.
Fix: Redis TTL = inbox TTL. Strip attachments >500 KB, store only metadata. Purge job runs via EVALSHA Lua script nightly.
Mistake: No Idempotency on Webhook Delivery
Why It Hurts: Retries duplicate test triggers — Playwright runs signup flow twice, corrupting analytics.
Fix: Include X-Idempotency-Key: inbox:{id}:msg:{ulid} in webhook payload. Consumers must deduplicate on this key.
Pro Tips
- Pre-warm 5 IPs per region; rotate via DNS round-robin with 60-second TTL to isolate reputation incidents.
- Use ULID (not UUIDv4) for inbox IDs — sortable, encodes timestamp, enables range queries in Redis sorted sets.
- Offer GraphQL subscription
onMessage(inboxId: ID!)alongside REST for real-time test debugging. - Sign outbound webhook payloads with Ed25519; publish JWKS endpoint so clients verify without shared secrets.
- Run
swaks --to test+load@domain --from ci@agency --server mx1 --tlsin CI to verify end-to-end latency <800ms p99.
FAQ
What is a temporary email service backend?
A temporary email service backend is a server-side system that receives SMTP mail for auto-expiring addresses, stores messages in ephemeral storage (typically Redis with TTL), and exposes APIs for programmatic inbox creation and message retrieval. Unlike public disposable providers, it runs on infrastructure you control, enabling custom retention, webhook delivery, and compliance guarantees.
How does Haraka compare to Postfix for a disposable email service?
Haraka handles 2× more concurrent connections per vCPU than Postfix and allows plugin development in JavaScript/TypeScript within hours. Postfix uses less memory per connection (95 MB vs 180 MB per 1k) and has broader enterprise adoption, but requires C milter expertise for custom logic. For agency teams without dedicated mail engineers, Haraka's velocity advantage outweighs Postfix's memory efficiency.
Can I build this without managing my own SMTP servers?
Yes — use a transactional email API (SendGrid, Mailgun, AWS SES) as the inbound edge via their webhook/parse endpoints. Trade-off: you lose control over IP reputation, pay per-message ($0.001–$0.004), and cannot enforce custom SPF/DKIM on the receiving side. For <100k messages/month, API-based ingress is viable; beyond that, self-hosted MTA wins on cost and flexibility.
Why are my test emails landing in spam folders?
Likely causes: missing or misaligned DKIM/SPF/DMARC on your sending domain, IP reputation below threshold (check Google Postmaster Tools), or message content triggering Bayesian filters. Fix: align From: domain with DKIM d= and SPF ip4:, warm IPs over 3 weeks, and test with mail-tester.com scoring before production traffic.
What happens to messages after the inbox TTL expires?
Redis automatically evicts keys when their TTL reaches zero — no application logic required. A nightly Lua script scans for orphaned keys (inboxes created but never accessed) and purges associated message hashes and sorted sets. Attachments offloaded to S3/MinIO are deleted via lifecycle policy (1 day after Redis key expiry). Zero manual cleanup.
Conclusion
Building a temporary email backend gives agencies deterministic test infrastructure — no more flaky CI runs from address collisions, no GDPR surprises from third-party retention, and full observability from SMTP handshake to webhook delivery. Start with Haraka on two nodes, Redis for ephemeral storage, and a Fastify API behind NGINX. Warm your IPs methodically, enforce DMARC on every inbound message, and instrument every hop with correlation IDs. The result: a service that scales to millions of test emails per month while your QA team focuses on shipping features, not debugging mail flow.
- Haraka + Redis + Fastify = production-ready in 2 sprints, not 6 months
- Per-client subdomains + dedicated DKIM selectors isolate reputation risk
- TTL-driven Redis eviction + Lua purge jobs = zero ops burden for cleanup
- Webhook idempotency keys + Ed25519 signatures = reliable CI/CD integration
0 comments:
Post a Comment