On this page
In the post on free professional email, I covered the pragmatic setup: Cloudflare Email Routing for inbound, Resend for outbound, done in an afternoon. This post is the maximalist sequel — going from routing email to running the actual email client on Cloudflare Workers.
The base is a fork of the open-source email-explorer project — my fork lives at github.com/ImadRashid/email-explorer. Out of the box it’s a self-hosted email client built for the Workers platform; my fork adds multi-provider outbound sending and an AI drafting feature. Here’s the architecture, the cost math I did before committing, and what I changed.
The receiving half: Email Routing into a Worker
Cloudflare Email Routing is usually described as a forwarding service — mail for you@yourdomain.com forwarded to a mailbox you already have. The under-advertised feature is that a routing rule can deliver messages to a Worker instead of forwarding them. I covered the wiring details in a TIL; the short version:
- Enable Email Routing on the domain (this takes over your MX records).
- Create a route whose destination is a Worker rather than a forwarding address.
- The Worker receives each inbound message and can parse and store it — at which point your mail is data in your own storage, and a web UI over that storage is an email client.
That’s the receiving half of an email service, running entirely inside Cloudflare’s free tier: no mail server, no IMAP daemon, no port 25 anywhere in sight.
The cost analysis I did before deploying
Before running my mail through this, I sat down and did the free-tier math — the habit that underpins my whole $0 stack. Personal email volume is tiny compared to any Workers ceiling: the free tier’s request allowance dwarfs the traffic an email UI plus inbound message deliveries generate for one person, and storage for years of personal mail sits comfortably inside free allowances.
The one place the free tier said no was outbound — which shaped the fork.
The sending half: one interface, three providers
Here’s the asymmetry at the heart of self-hosted email on Workers: receiving is free, sending is not. Cloudflare-native email sending from Workers requires a paid plan (TIL on that discovery). And even setting cost aside, I’d never self-host outbound delivery anyway — deliverability from arbitrary infrastructure is a fight you lose before it starts.
So my fork adds multi-provider outbound: a single send interface with pluggable providers behind it —
- Resend — my default, same as the rest of my projects; the free tier absorbs personal volume easily.
- Maileroo — a second API-based provider, so the client isn’t married to one vendor’s free tier or uptime.
- Cloudflare — supported for completeness, for deployments on plans where native sending is unlocked.
The design point worth stealing has nothing to do with email: when a platform makes one capability paid or restricted, wrap that capability in an interface and make the provider a configuration choice. The fork stays deployable by anyone, whatever their plan or preferred sender, and no provider becomes load-bearing.
The AI drafting feature: BYOK on purpose
The other addition is AI-assisted email drafting, and the architecture decision matters more than the feature: it’s bring-your-own-key. A user pastes their own OpenRouter API key, the key is stored server-side in their deployment, and drafting calls run on their account at their cost.
For a self-hosted tool, BYOK is the only honest shape for an AI feature. The alternative — the project owner proxying and paying for everyone’s AI calls — recreates exactly the dependency self-hosting exists to avoid. With BYOK, the tool ships the capability; the economics stay with the user; and anyone who doesn’t want AI near their mail simply doesn’t add a key, and nothing phones anywhere.
Honest limits
Is this better than the simple Email Routing + Resend setup from the earlier post? For most people, no — that setup is an afternoon and mostly maintenance-free, and it’s what I’d recommend first. This is for the next tier of wanting: your mail archive in your own storage under your own domain logic, a client you can modify because you have, and no mailbox provider in the loop at all.
What you give up is what you always give up self-hosting: you’re the ops team now. And what you keep is the reason to do it — the entire thing runs on infrastructure that costs nothing, built from a fork you control, one wrangler deploy from any change you care to make.