Skip to content

sluhtie/freesend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Freesend

Self-hosted, open-source, Resend-compatible transactional + marketing email.

Freesend is an email platform you run on your own server, speaking a Resend-compatible REST API — point an existing Resend SDK at your Freesend instance by changing only the base URL and key prefix from re_ to fs_ — and providing a full dashboard for emails, domains (DKIM/SPF/DMARC), API keys, webhooks, templates, and broadcasts. Delivery is bring-your-own SMTP: configure credentials for any provider (Amazon SES, Postmark, Mailgun, self-hosted Postfix) and Freesend DKIM-signs and relays through it. No SaaS subscription, no per-email markup — just Docker + your infrastructure.

Features

Sending

  • Transactional emails with HTML + text bodies
  • Batch sends (up to 100 emails per request)
  • Scheduled sending (future delivery via natural language or ISO dates)
  • Attachments (base64 inline or remote URL)
  • Custom headers and tags for categorization
  • Idempotent requests (24h TTL, Idempotency-Key header)
  • Per-API-key rate limiting with token bucket

Domains & DKIM

  • Domain registration and verification
  • Automatic RSA-2048 DKIM key generation
  • Per-domain DNS records (DKIM, SPF, DMARC) with checklist status
  • Hard-block sending from unverified domains
  • Per-domain or team-default SMTP credentials

Templates

  • MJML templates (responsive, SQL-backed)
  • Logic-less Handlebars templating with variable support
  • Template versioning and drafts
  • Dynamic rendering at send time

Webhooks & Feedback

  • Outbound webhook delivery with svix-style signatures + retries
  • Inbound provider feedback: Amazon SES (SNS, cert-verified) + a generic token-authed endpoint any provider (Postmark, Mailgun, …) can post to
  • Bounce/complaint/delivered/opened/clicked event collection (bounces & complaints auto-suppress)
  • Webhook retry with exponential backoff

Audiences & Broadcasts

  • Contact audiences with CSV import
  • Suppression list (bounces, complaints, unsubscribes)
  • Broadcast sends (one call, many recipients)
  • Per-contact unsubscribe tracking

Analytics & Tracking

  • Event timeline per email (sent, delivered, bounced, complained, opened, clicked)
  • Open tracking (pixel beacon)
  • Click tracking (rewritten links)
  • Email + event logs searchable via dashboard

Operations

  • PostgreSQL-only required stateful service (no Redis by default)
  • Postgres-backed job queue (pg-boss) for sends, retries, webhooks
  • Optional Redis/BullMQ for high-throughput deployments
  • AES-256-GCM envelope encryption of DKIM keys and SMTP passwords
  • Single Docker image with role dispatch (web, worker, migrate)
  • Auth.js v5 dashboard authentication
  • Bearer API keys (hashed HMAC-SHA256, one-time display)
  • Multi-team isolation (shared DB, application-enforced scoping)

Quickstart

# 1. Clone the repo
git clone https://github.com/freesendapp/freesend
cd freesend

# 2. Create .env and generate secrets
cp .env.example .env
npm run gen:keys   # outputs three secrets; paste them into .env

# 3. Set a strong Postgres password
# Edit .env and set: POSTGRES_PASSWORD=your-strong-password

# 4. Start the full stack (postgres + web + worker)
docker compose up -d
#   or with `just` installed:
#   just bootstrap

# 5. Open the dashboard and create the first admin
open http://localhost:3000   # redirects to /setup on first run

Local development

npm install
npm run gen:keys              # paste secrets into .env
docker compose up -d postgres # start just the database
npm run db:migrate            # apply migrations
npm run dev                   # WORKER_IN_PROCESS=true runs the worker in-process

Send your first email

Once you've created an admin account, add SMTP credentials, verify a domain, and create an API key from the dashboard. Then:

curl -X POST http://localhost:3000/api/v1/emails \
  -H "Authorization: Bearer fs_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "noreply@yourdomain.com",
    "to": "user@example.com",
    "subject": "Welcome",
    "html": "<p>Hello from Freesend!</p>"
  }'

Point a Resend SDK at Freesend

import { Resend } from 'resend'
const resend = new Resend('fs_live_xxx', { baseUrl: 'https://mail.example.com/api/v1' })
await resend.emails.send({
  from: 'noreply@yourdomain.com',
  to: 'user@example.com',
  subject: 'Hi',
  html: '<p>Hello</p>',
})

Essential configuration

Env var Purpose Example
POSTGRES_PASSWORD Postgres credentials (set in .env, docker-compose expands it) your-strong-password
APP_URL Public URL (for links, DNS hints, webhook callbacks) https://mail.example.com
AUTH_SECRET Auth.js session secret (≥32 bytes, from npm run gen:keys) ...base64...
FREESEND_ENCRYPTION_KEY 32-byte base64 master key for DKIM + SMTP secret encryption. Loss = unrecoverable; leak = total compromise. ...base64...
API_KEY_PEPPER Server-held pepper for API key hashing (from npm run gen:keys) ...base64...
QUEUE_DRIVER pg (default, no Redis) or redis (requires REDIS_URL) pg
ALLOW_SIGNUP Allow public signup (false default) false
SETUP_TOKEN Optional token protecting /setup on internet-exposed hosts (unset)

See docs/configuration.md for the full environment reference.

Documentation

  • ARCHITECTURE.md — Design decisions, data model, API surface, security, roadmap, deployment patterns
  • docs/configuration.md — Every environment variable and its constraints
  • docs/deployment.md — Docker/compose, scaling (worker, web replicas, Redis opt-in), key rotation, backups, migrations, retention policies
  • docs/api.md — REST API reference (Resend-compatible endpoints, request/response shapes, auth)
  • docs/sending.md — Domain setup + DKIM/SPF/DMARC, sending (HTML/text, attachments, scheduling, batch, idempotency), templates (MJML, Handlebars), tracking
  • docs/webhooks.md — Outbound webhook signing + verification, inbound provider feedback (SES SNS + generic token endpoint)
  • docs/broadcasts.md — Audiences, contacts, CSV import, broadcasts, suppression, unsubscribe

Stack

  • Next.js 16 (App Router) + React 19 + TypeScript — API and dashboard in one app
  • PostgreSQL 17 — the only required stateful service
  • Drizzle ORM with committed SQL migrations
  • pg-boss — Postgres-backed job queue (sends, retries, webhooks)
  • nodemailer — SMTP delivery + DKIM signing
  • Auth.js v5 — dashboard authentication
  • AES-256-GCM — envelope encryption for secrets at rest

How it works

You run one Docker image across one or more containers (web, worker, migrate roles). The web container listens for API + dashboard requests, validates with your API keys, and queues emails into Postgres. The worker pulls jobs, renders templates, DKIM-signs via nodemailer, relays through your BYO SMTP, and records delivery status. Webhooks from your mail provider update the event timeline and trigger customer webhooks. See ARCHITECTURE.md for the full design.

License

MIT

About

Self-hosted, Resend-compatible transactional + marketing email platform. BYO SMTP, DKIM, webhooks, templates, broadcasts — one Docker image, Postgres-only.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages