Sunday, August 9, 2026

Build a Temporary Email Service Backend: Step-by-Step Guide

Over 40% of internet users rely on disposable email addresses to protect their primary inbox from spam and data breaches, according to 2023 privacy surveys. Developers building privacy tools, testing platforms, or signup-flow validators need a reliable temporary email backend — but most tutorials skip the hard parts: mail delivery, storage expiration, and deliverability. This guide walks you through every layer, from MTA configuration to API design, using battle-tested open-source components that power production services like Guerrilla Mail and Temp Mail.

Quick Answer: Deploy Postfix as your MTA to receive SMTP mail, Dovecot for IMAP/POP3 access, store messages in Maildir or a database with TTL-based expiration, implement sub-addressing (plus addressing) for instant address generation, add SpamAssassin filtering, configure DKIM/SPF/DMARC for outbound reputation, and expose a REST API for frontend integration — all on a single Linux server with Docker.

Architecture Overview: Why This Stack Works

Core Components and Their Roles

A temporary email service needs three capabilities: accept incoming mail via SMTP, store messages with automatic expiration, and serve them via API or IMAP. Postfix handles SMTP reception — it's the default MTA on Ubuntu, RedHat, and macOS, actively maintained since 1997 by Wietse Venema. Dovecot provides IMAP/POP3 access and holds 76.9% of the IMAP server market share as of 2020. For storage, Maildir format (one file per message) simplifies expiration logic compared to mbox. This combination avoids the complexity of proprietary systems while scaling to millions of inboxes.

Sub-Addressing for Instant Address Generation

Instead of creating database records for each temporary address, use sub-addressing (plus addressing): user+randomtag@yourdomain.com routes to the same mailbox as user@yourdomain.com. Postfix natively supports this via recipient_delimiter = + in main.cf. A single catch-all mailbox receives all mail; your application parses the tag to identify the intended inbox. This eliminates per-address provisioning latency and reduces storage overhead — Guerrilla Mail has used this pattern since 2006.

Expiration Strategy: TTL vs. Cron vs. Database

Messages must self-destruct. Three approaches exist: filesystem TMPFS with atime-based cleanup (simple but loses data on reboot), database TTL column with background worker (flexible, queryable), or Maildir + cron deleting files older than N minutes (zero dependencies). For a first build, Maildir + cron every 5 minutes deleting files under /var/mail/temp/{inbox}/cur/ older than 60 minutes balances simplicity and reliability. Production services like 10MinuteMail use database-backed TTL for audit trails.

Server Provisioning and MTA Configuration

Base System Requirements

Provision a VPS with 2 vCPU, 4 GB RAM, 50 GB SSD (DigitalOcean, Linode, or Hetzner). Ubuntu 22.04 LTS provides 5 years of security updates. Install Docker and Docker Compose for service isolation. Configure reverse DNS (PTR record) matching your mail domain — major providers reject mail from IPs without valid PTR. Set hostname to mail.yourdomain.com and update /etc/hosts. Open ports 25 (SMTP), 587 (submission), 143 (IMAP), 993 (IMAPS), and 80/443 for API.

Postfix main.cf Essentials

Key settings in /etc/postfix/main.cf: myhostname = mail.yourdomain.com, mydomain = yourdomain.com, myorigin = $mydomain, inet_interfaces = all, mydestination = $myhostname, localhost.$mydomain, localhost, relay_domains =, recipient_delimiter = +, virtual_alias_maps = hash:/etc/postfix/virtual, virtual_mailbox_domains = yourdomain.com, virtual_mailbox_base = /var/mail/vhosts, virtual_mailbox_maps = hash:/etc/postfix/vmailbox, virtual_minimum_uid = 5000, virtual_uid_maps = static:5000, virtual_gid_maps = static:5000. Run postmap on virtual and vmailbox files after edits. The catch-all entry @yourdomain.com tempcatchall in virtual routes all mail to one Linux user.

Dovecot Configuration for IMAP Access

In /etc/dovecot/conf.d/10-mail.conf: mail_location = maildir:/var/mail/vhosts/%d/%n. In 10-auth.conf: disable plaintext auth unless SSL/TLS, auth_mechanisms = plain login. In 10-master.conf: configure LMTP socket for Postfix delivery (unix:listener /var/spool/postfix/private/dovecot-lmtp). Create vmail user (UID 5000) owning /var/mail/vhosts. Test with telnet localhost 143 and login as tempcatchall@yourdomain.com. Dovecot's 2017 Mozilla audit found only three minor issues — it's production-hardened.

Storage, Expiration, and API Layer

Maildir Structure and Message Parsing

Each message lands in /var/mail/vhosts/yourdomain.com/tempcatchall/cur/ as a unique file with flags (e.g., 1704067200.M123456P7890.mail:2,S). Your API worker reads these files, parses headers (From, To, Subject, Date, Message-ID) and body using Python's email.parser or Go's mime/multipart. Extract the tag from the To address (user+tag@domain → tag) to route to the correct virtual inbox. Store parsed metadata in Redis or PostgreSQL: inbox_id, message_id, from_addr, subject, body_text, body_html, received_at, expires_at. Index on inbox_id and expires_at for fast queries.

Automatic Expiration Implementation

Option A: Cron job every 5 minutes running find /var/mail/vhosts/yourdomain.com/tempcatchall/cur -type f -mmin +60 -delete. Option B: Database worker deleting rows WHERE expires_at < NOW() and removing corresponding Maildir files. Option C: Redis keys with EXPIRE set to 3600 seconds; background job syncs to persistent DB for analytics. For 10k+ inboxes/hour, Option B with a connection-pooled PostgreSQL worker (pgx pool, 10 workers) handles load without filesystem thrashing. Log deletions for audit: inbox_id, message_count, freed_bytes.

REST API Design for Frontend Integration

Endpoints: GET /api/v1/inboxes/:id/messages (list with pagination), GET /api/v1/inboxes/:id/messages/:msg_id (full message), POST /api/v1/inboxes (create new tag, returns address), DELETE /api/v1/inboxes/:id (manual purge), GET /api/v1/stats (total inboxes, messages, storage). Rate limit: 60 req/min per IP via nginx limit_req_zone. Return JSON with consistent envelope: { data: {}, meta: {} }. Use UUIDv7 for inbox IDs (timestamp-embedded, sortable). Deploy API as Go/Node/Python container behind nginx with TLS termination.

Deliverability, Security, and Abuse Prevention

DKIM, SPF, and DMARC Setup

Generate DKIM key: opendkim-genkey -b 2048 -d yourdomain.com -s mail -v. Publish public key in DNS TXT record mail._domainkey.yourdomain.com. SPF record: v=spf1 ip4:YOUR_SERVER_IP -all. DMARC record: v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com; ruf=mailto:dmarc@yourdomain.com; sp=quarantine; adkim=s; aspf=s. Test with mail-tester.com — aim for 10/10. Without these, Gmail and Outlook route temp mail to spam or reject outright. Postfix integrates with OpenDKIM via milter: smtpd_milters = inet:localhost:8891, non_smtpd_milters = inet:localhost:8891.

SpamAssassin Integration

Install spamassassin and spamc. In Postfix master.cf, add content_filter = spamassassin and define spamassassin unix - n n - - pipe user=debian-spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}. Configure local.cf: required_score 5.0, use_bayes 1, bayes_auto_learn 1. Tag spam with X-Spam-Status header; your API filters messages scoring ≥ 5 into a separate quarantine table. This catches 95%+ of abusive signups while letting legitimate verification emails through. Retrain Bayes monthly with sa-learn --ham/--spam on user-reported samples.

Rate Limiting and Abuse Controls

Implement three layers: (1) Postfix smtpd_client_connection_rate_limit = 10, smtpd_client_message_rate_limit = 20 per minute. (2) API: nginx limit_req_zone $binary_remote_addr zone=api:10m rate=30r/m; limit_req zone=api burst=10 nodelay. (3) Application: track inbox creations per IP in Redis with 1-hour sliding window; block at 50/hour. Monitor for patterns: same From address hitting 100+ inboxes (credential stuffing), or inboxes receiving only spam (honeypot detection). Log all blocks with IP, timestamp, reason for forensic analysis.

Comparison: Self-Hosted vs. Managed vs. Serverless

Choosing a deployment model affects cost, control, and maintenance burden. The table below compares three approaches for a service handling 100,000 inboxes/month.

Key trade-offs: self-hosted gives full data ownership and lowest marginal cost but requires ops expertise; managed email APIs (Mailgun, SendGrid) reduce ops but charge per-message and retain data; serverless (Cloudflare Workers + Email Workers) eliminates servers but has cold-start latency and vendor lock-in.

FactorSelf-Hosted (Postfix + Dovecot)Managed API (Mailgun, SendGrid)Serverless (Cloudflare Email Workers)
Monthly Cost (100k inboxes)$20-40 (VPS)$350-800 (per-message fees)$5-15 (requests + storage)
Data Retention ControlFull (your TTL logic)Vendor policy (30-90 days default)Partial (KV TTL, 30-day max)
Deliverability ManagementManual (DKIM/SPF/DMARC)Shared IP pools, dedicated IP extraCloudflare managed, limited control
Latency (receive → API available)50-200ms200-500ms (webhook + processing)100-300ms (edge + queue)
Ops OverheadHigh (OS, MTA, monitoring)Low (API only)Very Low (code only)
Custom Expiration LogicUnlimitedWebhook + your workerLimited by Workers CPU time

Common Mistakes and Expert Fixes

Mistake: Using a Single Catch-All Without Tag Parsing

Routing all mail to one inbox without extracting the sub-address tag makes it impossible to separate user inboxes. Every request returns every message. Fix: parse the recipient_delimiter (+) in your API worker — split on @, then split local-part on +, the second segment is the inbox identifier. Store this at ingestion time, not query time.

Mistake: Ignoring PTR and Reputation

Deploying on a fresh VPS IP without reverse DNS causes immediate blocking by Gmail, Outlook, Yahoo. Fix: request PTR from your cloud provider before sending test mail. Warm the IP: send 50/day to verified addresses for two weeks, monitor mail-tester.com and Google Postmaster Tools. Register for Feedback Loop (FBL) with major ISPs.

Mistake: No Message Deduplication

Retransmissions (network glitches, sender retries) create duplicate messages with same Message-ID. Users see double notifications. Fix: enforce UNIQUE constraint on (inbox_id, message_id) in your database. On INSERT conflict, update received_at only. Log deduplication events for analytics.

Mistake: Storing Full Raw Email in Database

Saving entire MIME messages (attachments, inline images) bloats storage 10-100x. A 5 MB PDF in 10k inboxes = 50 GB. Fix: parse and store only headers + text/html bodies. Offload attachments to S3-compatible storage (MinIO, R2) with presigned URLs in API response. Set lifecycle policy: delete attachments after 24 hours.

Pro Tips

  • Use UUIDv7 for inbox IDs — timestamp-embedded, sortable, no coordination needed across workers.
  • Deploy a health-check endpoint that sends a test email to itself via local Postfix and verifies API returns it within 5 seconds.
  • Implement webhook callbacks (POST to user URL on new message) for integration with testing frameworks — add HMAC signature for verification.
  • Run Postfix in a separate container from Dovecot and API — failure isolation prevents mail queue backup during API deployments.
  • Monitor queue depth with postqueue -p exposed via Prometheus; alert if > 1000 messages (indicates delivery loop or disk full).

FAQ

What is a temporary email service backend?

A temporary email service backend is the server-side infrastructure that receives, stores, and serves disposable email addresses with automatic expiration. It comprises an MTA (Postfix) for SMTP reception, a storage layer (Maildir or database) with TTL-based cleanup, and an API for frontend access. Unlike forwarding-based aliases, it hosts actual inboxes that expire after a set period, typically 10-60 minutes.

How does sub-addressing differ from creating real email accounts?

Sub-addressing (user+tag@domain) routes to a single catch-all mailbox without provisioning individual accounts in the MTA. Real accounts require virtual_mailbox_maps entries, quota management, and authentication records per address. Sub-addressing scales to millions of tags with zero MTA configuration changes — the application layer handles routing via tag parsing.

Can I build this without managing my own mail server?

Yes. Use an inbound email API like Mailgun Routes, SendGrid Inbound Parse, or Cloudflare Email Workers. They receive mail via webhook, you store and expire messages in your database. Trade-offs: per-message cost ($0.001-0.003), vendor data retention policies, and shared IP reputation. For >50k messages/month, self-hosted becomes cheaper.

Why do my test emails go to spam or get rejected?

Missing PTR record, no DKIM/SPF/DMARC, IP on blocklist (Spamhaus, Barracuda), or sending from residential/cloud IP ranges flagged by ISPs. Fix: verify PTR matches hostname, publish all three DNS records, check IP at mxtoolbox.com/blacklists.aspx, request delisting if listed. Warm new IPs gradually over 14 days.

What happens when temporary email domains get blocklisted?

Major sites (GitHub, Twitter, banks) maintain blocklists of known DEA domains. If your domain appears, signups fail. Mitigation: rotate domains weekly via automated registration (Namecheap/GoDaddy API), use aged domains with clean history, or offer custom domain feature where users point their own subdomain to your service.

Conclusion

Building a temporary email backend from scratch gives you full control over data retention, deliverability, and cost — critical for privacy tools, QA platforms, and spam research. The Postfix + Dovecot + Maildir stack has powered production services for 15+ years with minimal dependencies. Start with a single VPS, implement sub-addressing for instant provisioning, add database-backed expiration for reliability, and layer DKIM/SPF/DMARC for inbox placement. Monitor queue health, deduplicate by Message-ID, and offload attachments to object storage. The result: a service handling 100k+ inboxes/month for under $40/month with zero vendor lock-in.

  • Use sub-addressing (+tag) to eliminate per-address MTA configuration
  • Implement database-backed TTL expiration for auditability and reliability
  • DKIM/SPF/DMARC are non-negotiable for deliverability to major providers
  • Offload attachments to object storage; store only parsed metadata in DB

Sources

Share:

0 comments:

Post a Comment