A self-hosted, open-source alternative to Resend for sending transactional emails.
FreeResend allows you to host your own email service using Amazon SES and optionally Digital Ocean for DNS management. It provides a Resend-compatible API so you can use it as a drop-in replacement.
📰 Stay updated: Get the latest frontend development insights delivered weekly with Frontend Weekly - curated by the author of FreeResend!
🧭 Need a production rollout checklist? FreeResend stays MIT-licensed and free to self-host. The optional $12 Self-Hosted Launch Kit gives you a DNS, SES, webhook, smoke-test, and rollback checklist while supporting the project. You can also buy it directly on Stripe.
🔎 Want a second set of eyes before launch? The optional $12 FreeResend Deployment Review is a narrow manual review of one self-hosted rollout plan. Stripe collects your deployment URL or GitHub issue and main SES/DNS concern. You can also book it directly on Stripe.
📬 Checking DNS before SES launch? Run the free Email DNS Readiness Checker for SPF, DMARC, DKIM, and MX records before sending production traffic.
📨 Need SES production access? Use the free SES Production Request Helper to draft a reviewer-friendly request without sharing secrets or customer data.
git clone <your-repo>
cd freeresend
npm install
cp .env.local.example .env.local
Edit .env.local with your configuration:
# Next.js Configuration
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-super-secret-jwt-key-here
# Database Configuration (PostgreSQL)
DATABASE_URL=postgresql://username:password@hostname:port/database
# AWS SES Configuration
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-aws-access-key
AWS_SECRET_ACCESS_KEY=your-aws-secret-key
# Digital Ocean API Configuration (optional)
DO_API_TOKEN=your-digitalocean-api-token
# Application Configuration
ADMIN_EMAIL=admin@yourdomain.com
ADMIN_PASSWORD=your-secure-admin-password
In your Supabase SQL editor, run the contents of database.sql to create all necessary tables.
npm run dev
Visit http://localhost:3000 and log in with your admin credentials.
Verify your AWS account for SES:
Go to AWS SES console
Move out of sandbox mode if needed
Configure sending limits
Create IAM user with SES permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail",
"ses:VerifyDomainIdentity",
"ses:GetIdentityVerificationAttributes",
"ses:DeleteIdentity",
"ses:CreateConfigurationSet",
"ses:VerifyDomainDkim",
"ses:GetIdentityDkimAttributes"
],
"Resource": "*"
}
]
}
Note: The DKIM permissions (
ses:VerifyDomainDkim,ses:GetIdentityDkimAttributes) are required for automatic DKIM setup.
If you want automatic DNS record creation:
DO_API_TOKEN environment variableFreeResend is 100% compatible with the Resend Node.js SDK!
Set the RESEND_BASE_URL environment variable:
export RESEND_BASE_URL="https://your-freeresend-domain.com/api"
Then use Resend exactly as before:
import { Resend } from "resend";
// No changes needed - FreeResend API key works with Resend SDK!
const resend = new Resend("your-freeresend-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>",
});
const response = await fetch("https://your-freeresend-domain.com/api/emails", {
method: "POST",
headers: {
Authorization: "Bearer your-freeresend-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "onboarding@yourdomain.com",
to: ["user@example.com"],
subject: "Hello World",
html: "<strong>it works!</strong>",
}),
});
POST /api/auth/login - Login with email/passwordGET /api/auth/me - Get current user infoGET /api/domains - List all domainsPOST /api/domains - Add new domainDELETE /api/domains/{id} - Delete domainPOST /api/domains/{id}/verify - Check domain verificationGET /api/api-keys - List API keysPOST /api/api-keys - Create new API keyDELETE /api/api-keys/{id} - Delete API keyPOST /api/emails - Send emailGET /api/emails/logs - Get email logsGET /api/emails/{id} - Get specific emailPOST /api/webhooks/ses - SES webhook endpointDNS Records will be automatically created (if Digital Ocean is configured) or displayed for manual setup:
TXT record - _amazonses.yourdomain.com for SES domain verification
yourdomain.com for receiving emails via SESyourdomain.com for sender policy framework_dmarc.yourdomain.com for email authentication policyDKIM records - 3 CNAME records for *._domainkey.yourdomain.com for email signing
Verify domain - Click "Check Verification" once DNS records are live
FreeResend includes test scripts to verify your installation:
# Test with cURL (update variables in script first)
./test-curl.sh
# Test direct API + Resend SDK compatibility + Email logs
node test-email.js
Both scripts will:
Q: Getting "Invalid API key" errors
frs_keyId_secretPart (3 parts separated by underscores)Q: Digital Ocean DNS automation not working
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.digitalocean.com/v2/domainsQ: Domain verification stuck at "pending"
dig TXT _amazonses.yourdomain.comQ: AWS SES permissions error
ses:VerifyDomainDkim and ses:GetIdentityDkimAttributesQ: Resend SDK not working with FreeResend
export RESEND_BASE_URL="https://your-domain.com/api"frs_), not Resend API keyFROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
FreeResend can be deployed on Vercel with some configuration:
Note: Webhook endpoints might need special configuration for Vercel's serverless environment.
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Start production server
npm start
# Lint code
npm run lint
freeresend/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── api/ # API routes
│ │ │ ├── auth/ # Authentication endpoints
│ │ │ ├── domains/ # Domain management
│ │ │ ├── api-keys/ # API key management
│ │ │ ├── emails/ # Email sending & logs
│ │ │ └── webhooks/ # SES webhook handlers
│ │ ├── globals.css # Global styles
│ │ ├── layout.tsx # Root layout
│ │ └── page.tsx # Main dashboard page
│ ├── components/ # React components
│ │ ├── Dashboard.tsx # Main dashboard
│ │ ├── LoginForm.tsx # Authentication
│ │ └── *Tab.tsx # Tab components
│ ├── contexts/ # React contexts
│ └── lib/ # Core business logic
│ ├── supabase.ts # Database client
│ ├── auth.ts # Authentication logic
│ ├── ses.ts # Amazon SES integration
│ ├── digitalocean.ts # DNS automation
│ ├── domains.ts # Domain management
│ ├── api-keys.ts # API key logic
│ └── middleware.ts # API middleware
├── database.sql # Database schema
├── docker-compose.yml # Development setup
├── test-email.js # Comprehensive test script
├── test-curl.sh # Quick cURL test
└── README.md # This file
We welcome contributions! Here's how to get started:
git clone https://github.com/eibrahim/freeresend.gitnpm installnode test-email.jsnpm run devgit checkout -b feature/your-feature-nameWhen reporting bugs, please include:
MIT License - see LICENSE file for details.
FreeResend is built and maintained by Emad Ibrahim - a software engineer and entrepreneur passionate about creating developer tools and open-source solutions.
$ claude mcp add freeresend \
-- python -m otcore.mcp_server <graph>