I wanted a throwaway email service that wasn't slow, wasn't covered in ads, and worked in my language. Nothing I found checked all three boxes. So I built one.
The stack is Haraka for SMTP, Redis for real-time message delivery, SQLite for storage, and Astro for the frontend deployed on Cloudflare Pages. The whole thing runs on a single VPS plus Cloudflare's edge network.
Why Haraka
Most guides for "build your own email server" point you at Postfix or something similar. I went with Haraka because it's Node.js, which meant I could write plugins in a language I already knew instead of learning Postfix configuration files, which feel like they were designed to punish you.
Haraka handles inbound SMTP. When an email arrives, a custom plugin checks if the recipient address exists in the active session pool. If it does, the message gets parsed and pushed to Redis pub/sub. If it doesn't, the email gets rejected.
The plugin is about 80 lines. It extracts the sender, subject, HTML body, and attachments, then publishes a JSON payload to a Redis channel keyed by the recipient address.
Real-time delivery with Redis
The frontend opens a Server-Sent Events connection when you load the page. On the backend, there's a listener subscribed to the Redis channel matching your temporary address. When an email hits Haraka and gets published to Redis, the SSE connection pushes it to the browser immediately.
I tried WebSockets first but SSE was simpler for a one-directional flow. You don't need to send messages back to the server, you just need to receive emails. SSE reconnects automatically on connection drops, which is one less thing to handle.
SQLite for storage
Emails get stored in SQLite with a TTL. A background job runs every minute and deletes anything past its expiration. I considered Postgres but SQLite was enough for this scale and means one less service to manage on the VPS.
The schema is minimal: id, recipient, sender, subject, html_body, text_body, attachments (JSON), created_at, expires_at.
Astro on Cloudflare Pages
The frontend is Astro with islands of React for the interactive parts (inbox, email viewer, timer). Static pages (blog, about, privacy, terms, contact) are fully static HTML generated at build time, which is great for SEO.
I went with Cloudflare Pages because the deploy is just a git push. No Docker, no CI pipeline to maintain. The site builds in under 3 seconds for 400+ pages.
20 languages without a translation team
The site works in 20 languages. I used i18n JSON files with a structure like translations/en.json, translations/pt-br.json, etc. Each file has the same keys, different values. The routing is /[lang]/page, so /pt-br/blog gives you the Portuguese blog.
Blog posts are in Astro content collections, one markdown file per language per post. A single post exists as 20 files: en/what-is-temp-mail.md, pt-br/what-is-temp-mail.md, etc. It's a lot of files but the build handles it fine.
For the actual translations, I used AI to generate first drafts and then had native speakers review the top 5 languages by traffic. The long tail languages get fewer eyes but having them there at all helps with international SEO.
What I'd do differently
Redis pub/sub works but doesn't persist messages if no one is listening. If the user opens the page after the email already arrived, they won't see it unless it was also written to SQLite. I handle this by querying SQLite on page load and then switching to SSE for real-time updates. It works, but it means every email gets written twice (SQLite + Redis). In hindsight, I might use Redis Streams instead of pub/sub to get persistence and real-time in one place.
The site is trashbox.email if you want to try it. Source isn't open but I'm happy to answer questions about the architecture.
Top comments (0)