The default move for building an email platform in 2025 is to spin up some EC2 instances in us-east-1, put a queue in front of them, and call it a day. Or go serverless with Lambda, still in us-east-1, and add SQS for async processing.
We didn't do that. PushMail runs entirely on Cloudflare Workers, D1, KV, Queues, and R2. No AWS. No containers. No regions. Here's the reasoning.
V8 isolates vs Node.js processes
Cloudflare Workers don't run on Node.js. They run on V8 isolates — the same JavaScript engine that powers Chrome, but without the Node runtime overhead. A traditional Lambda function spins up an entire Node process, loads your dependencies, and then handles the request. A Worker starts in under 5 milliseconds because isolates share the runtime with other Workers on the same machine. There's no cold start in any meaningful sense.
For an email API, this matters. When your application calls our API to send an email or enroll a contact in a sequence, the response time is dominated by the platform overhead, not the business logic. A 200ms cold start on Lambda is fine once, but if your app is sending emails from a serverless function that scales to zero, you're stacking cold starts. Our API endpoint responds in the same time whether it's the first request of the day or the millionth.
Global by default
Workers deploy to every Cloudflare data center — over 300 locations in 100+ countries. When a developer in Tokyo calls the PushMail API, the request is handled by a Worker running in Tokyo. The API key is validated, the request is authorized, and the email is queued — all at the edge.
Compare this to Resend, which runs on AWS Lambda in a single region. Your API call from Tokyo routes across the Pacific to us-east-1, gets processed, and returns. That's 150-200ms of unnecessary latency on every request. SES has the same problem — you pick a region when you set up, and all your API calls go to that region regardless of where your users are.
For transactional email — password resets, order confirmations, 2FA codes — latency directly impacts user experience. The email send request should be acknowledged in milliseconds, not hundreds of milliseconds.
The full stack
We didn't just use Workers for compute. The entire platform runs on Cloudflare's stack:
D1 (SQLite at the edge) for relational data. Organizations, users, contacts, sequences, campaigns, templates, API keys — all stored in D1. It's SQLite under the hood, accessed through Drizzle ORM. No connection pooling to manage, no Postgres extensions to configure. Schema changes are applied through Drizzle migrations. For a platform like PushMail, where the data model is relational but the query patterns are straightforward, D1 is a perfect fit. We don't need full-text search or JSON operators — we need fast reads and reliable writes, and D1 delivers both.
KV for sessions and caching. Session tokens, API key lookups, rate limit counters, MX record caches, and the global bounce suppression list all live in KV. It's globally replicated with eventual consistency, which is fine for these use cases. A session doesn't need to be strongly consistent — it needs to be fast to read from any location.
Queues for async email processing. When you call our send endpoint, we don't call SendGrid synchronously. The email is validated, queued to Cloudflare Queues, and acknowledged. A separate queue consumer Worker picks up the message, renders the template, calls SendGrid, and deducts credits. This decouples the API response time from the actual sending. If SendGrid is slow or rate-limiting, the queue backs up gracefully instead of making your API calls hang.
We run two queues: one for email sends and one for CSV contact imports. Imports can be large (hundreds of thousands of rows), and processing them asynchronously means the upload API returns immediately while rows are parsed, validated, and upserted in the background.
R2 for storage. Email templates, rendered emails, uploaded CSVs, and image assets are stored in R2. It's S3-compatible, so any existing tooling works. No egress fees, which matters when you're serving template previews and email assets at scale.
Why edge-native matters for an email API
An email API is fundamentally a write-heavy, latency-sensitive service. Your application sends a request to queue an email, and it needs a fast acknowledgment so it can move on. The actual email delivery happens asynchronously.
This maps perfectly to edge computing. The write path — validate the request, check the API key, enqueue the message — can and should happen at the nearest data center. There's no reason for that request to cross an ocean.
The async path — render the template, call SendGrid, log the event — doesn't need to be at the edge. It just needs to be reliable. Cloudflare Queues handles that with at-least-once delivery and automatic retries.
Works from any runtime
Because PushMail's API is just HTTP, it works from any runtime without an SDK. No Node.js-specific package to install. Call it from:
- Vercel Edge Functions — same V8 isolate runtime, sub-millisecond overhead
- Deno Deploy — native fetch, no compatibility issues
- Bun — standard HTTP client
- Cloudflare Workers — Worker calling Worker, minimal latency
- Any server — curl, Python requests, Go net/http, whatever you have
curl -X POST https://pushmail.dev/api/v1/send \
-H "Authorization: Bearer pm_live_abc123..." \
-H "Content-Type: application/json" \
-d '{"to": "user@example.com", "template_id": "tmpl_xyz", "data": {"name": "Alice"}}'We'll ship language-specific SDKs eventually, but they'll be thin wrappers around HTTP. The API is the product.
The tradeoffs
This isn't a sales pitch — there are real tradeoffs. D1 is newer than Postgres and has a smaller ecosystem. Workers have CPU time limits (50ms on the free plan, 30 seconds on paid). You can't run a long-lived WebSocket connection or a background task that runs for minutes.
For PushMail, none of these are blockers. Email API requests are short-lived. Heavy processing happens in queue consumers with generous time limits. And D1's SQLite foundation means the query behavior is predictable and well-understood.
The result
PushMail's API responds in under 50ms from any location globally. Email sends are queued in single-digit milliseconds. There are no cold starts, no region selection, and no infrastructure to manage. The entire platform scales automatically with demand.
We didn't choose Cloudflare Workers because it's trendy. We chose it because the architecture of an email API — fast writes, async processing, global access — is exactly what edge computing is built for.