Self-host

Your AWS account is the server. One command.

You already have an AWS account, so OpenSES deploys into it: a Lambda, a bucket, a few topics and a CloudFront URL. Nothing to patch, nothing running when idle, no Docker. Bring any Postgres. Updating is running the same command again.

AGPL-3.0serverlessno Dockerany Postgres$0 idle
install
# 1. an AWS account and a Postgres URL (Neon's free tier is fine)
git clone https://github.com/openses/openses && cd openses

# 2. three questions
npx openses init
? AWS region     eu-central-1
? DATABASE_URL   postgres://…@ep-….neon.tech/openses
? Domain         mail.acme.com  (optional)

# 3. deploy
npx openses deploy

# → https://mail.acme.com · first visit creates the admin
# → the Lambda's own role is already connected to SES
What you get

Designed to be boring to operate

Serverless from end to end. SES publishes events straight into your Lambda, a cron runs sequences, Postgres holds the state. There is no process to keep alive.

Nothing to run

One Lambda serves the API, the MCP server and the event handlers. It scales to zero and back without you noticing.

Update with one command

git pull && npx openses deploy. Migrations run as part of the deploy and are backwards compatible across one minor version.

No keys at all

The Lambda's IAM role carries the SES permissions. Connect other AWS accounts with a role or keys if you need to, but your own needs neither.

Your Postgres

One DATABASE_URL. Keep bodies or metadata only, set your own retention, back it up like any database you already run.

In your account

What the deploy creates

All of it is defined in one sst.config.ts you can read. Remove it with one command and nothing is left behind except your SES identities, which were yours before.

  • Node 20+ and AWS credentials with permission to create the stack, once
  • A Postgres connection string
  • A domain if you want one; the CloudFront URL works without
Lambda function
API, MCP server, event and inbound handlers, cron. arm64, Node 22.
CloudFront + S3
The dashboard and a single HTTPS hostname. Custom domain optional.
SNS topics
One per region. SES publishes events straight into the Lambda.
S3 bucket
Inbound mail from SES receipt rules. Attachments never leave it.
EventBridge rule
Runs the cron every minute for sequences, scheduling and retries.
IAM role
The Lambda's own role carries the SES permissions. No keys exist.
Database

Any Postgres, one URL

No VPC, no security groups, no pooler to set up. Anything with a public TLS endpoint works from the Lambda as is. OpenSES uses Neon's HTTPS driver when it sees a Neon URL and a plain Postgres connection otherwise.

Neon
Free tier, scales to zero, HTTPS driver. The default in the docs.
Supabase
Pooler URL, nothing else to configure.
RDS / Aurora
Public endpoint works as is; private needs the VPC option.
Your own server
Any Postgres 15+ with a TLS endpoint.
Dashboard

Host the UI on AWS or on Cloudflare

The dashboard is a static build. By default it goes to S3 and CloudFront with the API. If you would rather have a free Cloudflare Worker with a one-click custom domain, point it at your API and run wrangler deploy. OpenSES Cloud runs this way.

deploy/cloudflare/wrangler.jsonc
{
  "name": "openses-dashboard",
  "assets": { "directory": "../../apps/web/dist" },
  "vars": { "API_URL": "https://api.acme.com" },
  "routes": [{ "pattern": "mail.acme.com", "custom_domain": true }]
}
Cost

Nothing when idle, pennies when busy

A team sending a few hundred emails a day stays inside the AWS free tiers. The only bill that grows with volume is Amazon SES, at $0.10 per thousand.

ServiceFree tierTypical monthly
Lambda1M requests + 400k GB-s free$0 – $1
CloudFront, S31 TB and 10M requests free$0
SNS → Lambda, cronFree$0
PostgresNeon free tier up to 0.5 GB$0 – $19
Amazon SES$0.10 per 1,000 emailsyour sending

Prefer not to run it?

OpenSES Cloud is the same code, operated by us, still on your AWS account. Fourteen days free, no card.