DEV Community

Cover image for How I built a zero-cost edge email pipeline using Cloudflare Workers
zerodrop
zerodrop

Posted on

How I built a zero-cost edge email pipeline using Cloudflare Workers

Every developer has hit this wall. You're building a password reset flow or an email verification step and you need a throwaway inbox to confirm the email actually arrives. Every existing tool either shows you ads, requires an account, or has an interface that looks like it was built in 2009.

So I built the infrastructure myself. Here's the architecture — and why each decision was made.

The core insight: email is just HTTP at the edge

Modern serverless platforms have made it possible to intercept inbound email the same way you'd intercept an HTTP request — with a lightweight function that runs at the edge, costs nothing at idle, and scales automatically.

Cloudflare Email Routing is the entry point. You configure a catch-all rule on a domain and every email sent to that domain — regardless of prefix — triggers a Worker function. anything@yourdomain.com, test-abc@yourdomain.com, qa-pipeline-7@yourdomain.com — all caught, zero per-address configuration.

The three-layer architecture

The system has three components that each do exactly one thing.

The routing domain. A cheap disposable domain — not your brand domain — that exists solely to catch email. The separation matters: if this domain ends up on a spam blocklist (and with disposable email services, it will eventually), you replace it for $10 without your main product going offline.

The Worker. When an email arrives, the Worker receives a message object containing the sender, recipient, headers, and the full raw MIME body as a readable stream. The Worker's job is simple: parse the relevant fields into a clean JSON structure and decide whether to store or drop it.

Upstash Redis. Emails are stored in a list keyed by inbox name — so test-abc@yourdomain.com maps to the Redis key inbox:test-abc. A TTL is set on every write. After 30 minutes the key expires automatically. No cleanup job, no stale data, no accumulating storage costs.

The decision that makes it economically viable: edge AI filtering

Without filtering, a routing domain gets discovered by bot farms within weeks of launch. They send thousands of automated emails — account farming scripts, scrapers, newsletter blasts. Each one costs a Redis write. At scale that burns through your free tier quota fast.

The fix is running a lightweight classification step inside the Worker before any database write happens. Using Cloudflare Workers AI with Llama 3, you can classify each email's sender and subject line in milliseconds. If the model flags it as automated spam, the Worker returns early — the email is silently dropped and never touches Redis.

The economics are stark. Cloudflare Workers AI inference at this scale costs fractions of a cent. A Redis write that gets skipped costs nothing. The filter pays for itself immediately.

The key architectural detail is the fallback: if the AI call fails for any reason, the email is allowed through. You never drop a legitimate developer test email because of an infrastructure error. Fidelity always wins over filtering.

What the free tier actually costs

Service Free tier First cost appears at
Cloudflare Email Routing Unlimited Never
Cloudflare Workers 100k req/day ~$5/month
Cloudflare Workers AI Generous free tier High volume
Upstash Redis 10k commands/day $0.2/100k commands
Vercel Generous free tier $20/month

At zero traffic the monthly bill is zero. The first real cost appears when you're processing tens of thousands of emails daily — at which point you have a real product with real revenue to cover it.

The two-domain strategy

One architectural detail worth highlighting separately: never use your brand domain for email routing.

The routing domain exists to be burned. When it gets blocklisted — and it will — you update one DNS record and replace it with a fresh domain. Your brand domain, your dashboard, your users' bookmarks — none of it is affected.

The brand domain is precious. The routing domain is disposable. Treat them accordingly.

The SDK problem

Once the pipeline works, the natural next step is making it usable in CI. A REST API is a specification. What QA engineers actually need is an abstraction that hides the polling loop, handles timeouts gracefully, and throws a meaningful error when an expected email never arrives.

The difference between a tool and infrastructure is that abstraction layer. A three-line integration that works in Playwright and Cypress without the developer understanding what's happening underneath — that's what makes something get embedded in codebases and never removed.

The specific design decision that matters: a timeout should throw a named error class, not return null. CI pipelines need explicit failures. A test that silently passes because waitForEmail() returned null and the assertion was never reached is worse than no test at all.

What I built with this architecture

After proving the pipeline worked I packaged it properly. The result is ZeroDrop — a free, ad-free temporary email sandbox at zerodrop.dev.

The npm package zerodrop-client wraps the polling logic for CI pipelines:

npm install zerodrop-client
Enter fullscreen mode Exit fullscreen mode
import { ZeroDrop } from 'zerodrop-client';

const mail = new ZeroDrop();
const inbox = mail.generateInbox();

// Use inbox in your test...

const email = await mail.waitForLatest(inbox, { timeout: 10000 });
expect(email.subject).toContain('Reset your password');
Enter fullscreen mode Exit fullscreen mode

The free tier uses a shared routing domain with AI filtering enabled. For teams who need custom domains, private storage, and unfiltered delivery — Workspaces are in private beta at zerodrop.dev.

The architecture in one sentence

Edge email interception → AI classification at the Worker level → Redis storage with TTL → polling API → SDK abstraction. Each layer does one thing and costs nothing until you have real scale.

Top comments (0)