Building FreeResend
A Self-Hosted, Open-Source Alternative to Resend
The Problem: Email Infrastructure Is Expensive
As a solopreneur building multiple SaaS products, I needed reliable transactional email. The options weren't great:
- Resend: Beautiful API and DX, but $20/month for 50K emails—adds up fast across multiple products
- SendGrid: Enterprise pricing, complex configuration, overkill for transactional use cases
- Postmark: Excellent deliverability but expensive per-email pricing
- Amazon SES Direct: Dirt cheap ($0.10/1K emails) but raw API, no dashboard, manual DKIM/DMARC setup
I wanted Resend's beautiful API with SES pricing. So I built it.
FreeResend: self-hosted email infrastructure with a 100% Resend-compatible API, running on your own AWS account.
The Vision: Drop-In Resend Replacement
The core principle was simple: change one environment variable, keep everything else the same. If you're using the Resend SDK, switching to FreeResend should be as easy as:
# Before (Resend)
RESEND_API_KEY=re_xxxxx
# After (FreeResend)
RESEND_BASE_URL=https://your-freeresend.com/api
RESEND_API_KEY=frs_xxxxx # Your FreeResend API key100% API Compatible
Same endpoints, same request/response format. The Resend SDK works out of the box—just point it at your server.
Your AWS Account
Emails send through your own SES. Full control, full visibility, SES pricing ($0.10/1K emails).
Automatic DKIM Setup
Domain verification and DKIM key generation handled automatically. Integrates with DigitalOcean DNS for zero-touch setup.
Open Source Forever
MIT licensed. Self-host on any platform—Vercel, Docker, Kubernetes. No vendor lock-in.
The Tech Stack: Simple and Production-Ready
Next.js 15 + TypeScript
The latest Next.js with Turbopack for fast development. API routes handle all email operations. TypeScript ensures type safety across the codebase—especially critical when matching Resend's API contract exactly.
PostgreSQL + Direct SQL
Chose raw SQL over Prisma for this project. The schema is simple (users, domains, api_keys, email_logs, webhook_events), and direct pg queries give full control. No ORM overhead, easy to understand what's happening at the database level.
AWS SDK v3 for SES
The modular AWS SDK v3 keeps bundle size small. Only import what you need: @aws-sdk/client-ses for email operations. Full support for DKIM verification, domain identity management, and both simple and raw email sending.
DigitalOcean DNS API (Optional)
If your domains are on DigitalOcean, FreeResend automatically creates all required DNS records—TXT for domain verification, CNAME for DKIM, MX, SPF, and DMARC. Zero manual DNS configuration. For other providers, records are displayed for manual setup.
JWT + bcrypt for Auth
Simple, secure authentication. Admin credentials stored with bcrypt hashing. JWT tokens for API access. API keys are hashed before storage—even if the database leaks, keys remain secure.
The Architecture: Resend API Contract
The key architectural decision: match Resend's API exactly. This means existing codebases using the Resend SDK work without modification.
API Endpoint Structure
POST /api/emails → Send email (Resend-compatible)
GET /api/emails/{id} → Get email details
GET /api/emails/logs → List email logs
POST /api/domains → Add domain for verification
GET /api/domains → List all domains
POST /api/domains/{id}/verify → Check domain verification status
DELETE /api/domains/{id} → Remove domain
POST /api/api-keys → Generate new API key
GET /api/api-keys → List API keys (masked)
DELETE /api/api-keys/{id} → Revoke API key
POST /api/webhooks/ses → SES SNS webhook endpointEmail Sending Flow
When an email is sent through FreeResend:
- API Key Validation: Extract key from
Authorization: Bearerheader, verify against hashed keys in database - Domain Check: Ensure sender domain is verified and associated with the API key
- Payload Validation: Zod schema validates request body matches Resend's expected format
- SES Send: Call AWS SES
SendEmailorSendRawEmailbased on content type - Log Creation: Store email metadata in
email_logstable with SES message ID - Response: Return Resend-compatible response with
{ id: messageId }
// Resend SDK works unchanged
import { Resend } from 'resend';
const resend = new Resend('frs_your_api_key');
const { data, error } = await resend.emails.send({
from: 'onboarding@yourdomain.com',
to: ['user@example.com'],
subject: 'Hello World',
html: '<strong>It works!</strong>'
});Domain Verification: The Hard Part Made Easy
Email deliverability depends on proper DNS configuration. Most developers dread setting up DKIM, SPF, and DMARC. FreeResend automates all of it.
What Gets Created
When you add a domain, FreeResend:
- TXT Record:
_amazonses.yourdomain.comfor SES domain verification - 3 CNAME Records:
*._domainkey.yourdomain.comfor DKIM signatures - MX Record: For receiving bounce notifications via SES
- SPF Record: Declares SES as authorized sender
- DMARC Record: Policy for handling authentication failures
Automatic DNS with DigitalOcean
If you provide a DigitalOcean API token, FreeResend creates all DNS records automatically:
// lib/digitalocean.ts
async function createDNSRecords(domain: string, records: DNSRecord[]) {
for (const record of records) {
await fetch(`https://api.digitalocean.com/v2/domains/${domain}/records`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DO_API_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: record.type,
name: record.name,
data: record.value,
ttl: 1800
})
});
}
}For other DNS providers, the dashboard displays all required records with copy buttons. Manual setup takes 5 minutes.
API Key Security: Never Store Plain Text
API keys are sensitive. A database leak shouldn't expose them. FreeResend uses a secure key generation pattern:
Key Structure
// Format: frs_{keyId}_{secretPart}
// Example: frs_abc123_xYz789defGHI...
const keyId = nanoid(6); // Public identifier
const secretPart = nanoid(32); // Secret portion
const fullKey = `frs_${keyId}_${secretPart}`;
// Only store the hash
const keyHash = await bcrypt.hash(secretPart, 10);
await db.query(
'INSERT INTO api_keys (id, key_hash, domain_id) VALUES ($1, $2, $3)',
[keyId, keyHash, domainId]
);
// Return full key ONCE - never stored in plain text
return { apiKey: fullKey };Validation Flow
When validating an API key:
- Parse the key: extract
keyIdandsecretPart - Look up record by
keyId(fast index lookup) - Compare
secretPartagainst stored hash with bcrypt - Return associated domain if valid
This pattern means: even if an attacker gets database access, they can't use the API keys. They'd need to crack bcrypt hashes first.
SES Webhooks: Real-Time Delivery Tracking
SES provides delivery notifications via SNS (Simple Notification Service). FreeResend captures these for email tracking:
- Delivery: Email accepted by recipient's mail server
- Bounce: Hard bounce (invalid email) or soft bounce (mailbox full)
- Complaint: Recipient marked email as spam
- Open: Email opened (if tracking pixel enabled)
- Click: Link clicked (if link tracking enabled)
// /api/webhooks/ses/route.ts
export async function POST(request: Request) {
const body = await request.json();
// Handle SNS subscription confirmation
if (body.Type === 'SubscriptionConfirmation') {
await fetch(body.SubscribeURL);
return NextResponse.json({ status: 'confirmed' });
}
// Parse notification
const message = JSON.parse(body.Message);
const { notificationType, mail } = message;
// Update email log status
await db.query(
'UPDATE email_logs SET status = $1, updated_at = NOW() WHERE ses_message_id = $2',
[notificationType.toLowerCase(), mail.messageId]
);
// Store webhook event for debugging
await db.query(
'INSERT INTO webhook_events (type, payload) VALUES ($1, $2)',
[notificationType, JSON.stringify(message)]
);
return NextResponse.json({ received: true });
}The Dashboard: Everything You Need
FreeResend includes a full admin dashboard for managing your email infrastructure:
Domain Management
- • Add/remove sending domains
- • View verification status
- • See required DNS records
- • One-click verification check
API Key Management
- • Generate keys per domain
- • View masked key list
- • Revoke compromised keys
- • Copy key on creation
Email Logs
- • Full send history
- • Delivery status tracking
- • Bounce/complaint alerts
- • Search and filter
Webhook Events
- • SES event log
- • Delivery confirmations
- • Bounce details
- • Debug payload viewer
The Challenges and Solutions
1. Matching Resend's API Contract
The Resend SDK expects specific response formats. I used Zod schemas to validate both incoming requests and outgoing responses match exactly. Any deviation breaks SDK compatibility. Extensive testing with real Resend SDK calls ensures parity.
2. DKIM Key Retrieval from SES
When you verify a domain with SES, it generates DKIM keys. But there's a race condition: callingGetIdentityDkimAttributes immediately afterVerifyDomainDkim returns empty tokens. Solution: retry with exponential backoff until tokens are available.
3. SNS Webhook Security
Anyone could POST fake webhook events to the endpoint. AWS signs SNS messages with a certificate. FreeResend validates the signature before processing. Additionally, message IDs are checked against known SES message IDs to prevent injection attacks.
4. API Key Display UX
API keys can only be shown once (we don't store plain text). Users were copying the masked version from the table instead of the full key shown on creation. Solution: prominent green success message with the full key, copy button, and clear warning that it won't be shown again.
The Results: Enterprise Email at SES Prices
Using Resend
- • Free tier: 3,000 emails/month
- • Pro: $20/month for 50K emails
- • Scale: $80/month for 100K emails
- Per project cost adds up fast
Using FreeResend + SES
- • SES: $0.10 per 1,000 emails
- • 50K emails: $5/month
- • 100K emails: $10/month
- Same API, 75-90% cost reduction
For 10 products sending 10K emails each: $200/mo with Resend vs $10/mo with FreeResend
Feature Parity with Resend
Technical Highlights
Deployment Options
FreeResend is designed for flexible deployment:
Vercel
One-click deploy. Connect repo, set environment variables, done. Serverless functions handle all API routes.
Docker
Dockerfile included. Run anywhere Docker runs—your VPS, AWS ECS, Google Cloud Run, or home server.
Kubernetes
Production-grade deployment with horizontal scaling, health checks, and rolling updates.
Conclusion: Own Your Email Infrastructure
FreeResend proves you don't need to choose between great developer experience and cost efficiency. By leveraging AWS SES as the delivery backbone and matching Resend's API exactly, you get:
- Drop-in replacement: Existing Resend SDK code works unchanged
- 90% cost reduction: SES pricing vs SaaS email services
- Full control: Your AWS account, your data, your infrastructure
- Open source: Inspect, modify, contribute—it's your code now
For solopreneurs and teams running multiple products, the savings compound quickly. And when you own the infrastructure, you never worry about API pricing changes or service shutdowns.
Email is critical infrastructure. Now you can own it completely.
Complete Tech Stack
Frontend: Next.js 15, React 19, TypeScript, Tailwind CSS, Lucide Icons
Backend: Next.js API Routes, PostgreSQL, JWT Authentication
Email: AWS SES (SendEmail, SendRawEmail), SNS Webhooks, DKIM/SPF/DMARC
DNS: DigitalOcean API (optional), Manual setup for other providers
Security: bcrypt for key hashing, Zod for validation, SNS signature verification
Deployment: Docker, Vercel, Kubernetes-ready
Ready to Own Your Email Infrastructure?
FreeResend is open source and ready to deploy. Check out the repo, star it, or reach out if you need help with custom email infrastructure.