Docs / Self-hosting

Self-hosting Mailgrid

MIT-licensed. Self-host on Cloudflare + AWS for ~$8/mo plus SES at-cost. Full setup in 15 minutes.

Prerequisites

Step 1 — Cloudflare resources

bash
export CLOUDFLARE_ACCOUNT_ID=<your-account-id>

wrangler d1 create inboxos                  # prints database_id
wrangler kv namespace create KV             # prints id
wrangler r2 bucket create inboxos-emails
wrangler queues create inboxos-webhooks
wrangler queues create inboxos-webhooks-dlq

Paste the printed database_id and KV id into wrangler.toml. Set account_id at the top.

Step 2 — Secrets

bash
openssl rand -hex 32 | wrangler secret put API_KEY_HMAC_SECRET
openssl rand -hex 32 | wrangler secret put UNSUBSCRIBE_SECRET
wrangler secret put AWS_ACCESS_KEY_ID
wrangler secret put AWS_SECRET_ACCESS_KEY

Save the API_KEY_HMAC_SECRET value — you'll need it again to seed the first tenant.

Step 3 — Migrate & deploy

bash
wrangler d1 migrations apply inboxos --remote
wrangler deploy

Step 4 — Seed first tenant

bash
node scripts/bootstrap-tenant.mjs \
  --name "Your Company" \
  --email "you@example.com" \
  --hmac-secret "$(your HMAC secret)" > /tmp/seed.sql

wrangler d1 execute inboxos --remote --file=/tmp/seed.sql

Save the printed mb_live_… token — it cannot be recovered.

Step 5 — AWS via Terraform (option A)

bash
cd infra/aws
cp terraform.tfvars.example terraform.tfvars
$EDITOR terraform.tfvars
./bootstrap.sh   # runs terraform + pipes IAM keys to wrangler secret

Step 5 — AWS manual (option B)

  1. SES → Verified identities → Create → Domain → enable Easy DKIM.
  2. Configuration sets → Create inboxos-prod.
  3. Event destinations → Add → SNS → create mailgrid-events topic.
  4. SNS → topic → Create subscription → HTTPS → https://api.your-domain/api/webhooks/ses.
  5. IAM → create user → policy scoped to ses:SendEmail, ses:SendRawEmail, ses:SendBulkEmail.
  6. Generate access keys → push to Wrangler.

Step 6 — Request SES production access

One-time AWS form. Until approved, you can only send to verified recipient addresses. SES → Account dashboard → Request production access.

Step 7 — Smoke test

bash
curl -X POST https://api.your-domain/api/emails \
  -H "Authorization: Bearer mb_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "from":"hello@your-domain", "to":"you@example.com", "subject":"first", "text":"it lives" }'

Cost estimate

Component10k emails/mo1M emails/mo
Cloudflare Workers Paid plan$5.00$5.00
D1 reads/writes$0.40~$3
R2 storage + ops$0.05~$2
KV reads$0.01$0.50
Durable Objects$0.15$5
Workers AI$1.00$10
AWS SES ($0.10/k)$1.00$100
Total$7.61~$125