Skip to main content

Overview

Sendook is open source and can be self-hosted for full control over your email infrastructure. The stack includes:
  • API - Node.js backend with Bun runtime
  • App - Nuxt 3 dashboard application
  • Landing - Nuxt 3 marketing website
  • Database - MongoDB for data persistence
  • Cache - Redis for session management (optional)

Quick Start with Docker

The fastest way to get Sendook running is with Docker.

Prerequisites

  • Docker and Docker Compose installed
  • MongoDB instance (local or hosted)
  • AWS account with SES configured
  • Stripe account (for billing)

Build and Run API

1. Build the Docker image
cd api
docker build -t sendook-api .
2. Run the container
docker run -p 8006:8006 \
  -e MONGO_URI="mongodb://localhost:27017/sendook" \
  -e DEFAULT_EMAIL_DOMAIN="sendook.com" \
  -e AWS_ACCESS_KEY_ID="your_aws_key" \
  -e AWS_SECRET_ACCESS_KEY="your_aws_secret" \
  -e STRIPE_SECRET_KEY="sk_test_..." \
  -e STRIPE_WEBHOOK_SECRET="whsec_..." \
  -e STRIPE_PRICE_ID="price_..." \
  -e STRIPE_PRODUCT_ID="prod_..." \
  -e STRIPE_METER_EVENT="email_sent" \
  sendook-api

Dockerfile Explanation

The API uses Bun’s official Docker image:
FROM oven/bun:latest AS base
WORKDIR /usr/src/app

# Install production dependencies
FROM base AS install
RUN mkdir -p /temp/prod
COPY package.json bun.lock /temp/prod/
RUN cd /temp/prod && bun install --frozen-lockfile --production

# Copy dependencies and source code
FROM base AS release
COPY --from=install /temp/prod/node_modules node_modules
COPY . .

ENV NODE_ENV=production

# Run the app
USER bun
EXPOSE 3000/tcp
ENTRYPOINT [ "bun", "run", "index.ts" ]
The Dockerfile exposes port 3000 internally, but the API listens on port 8006 by default. Map the ports accordingly.

Environment Variables

Required Variables

MongoDB

MONGO_URI="mongodb://localhost:27017/sendook"
Connection string for your MongoDB database.

Email Domain

DEFAULT_EMAIL_DOMAIN="sendook.com"
Default domain for creating inboxes without custom domains.

AWS Credentials

AWS_ACCESS_KEY_ID="AKIA..."
AWS_SECRET_ACCESS_KEY="your_secret_key"
AWS credentials with permissions for:
  • SES (Simple Email Service) - Send and receive emails
  • SES domain verification
  • SES configuration sets

Stripe Configuration

STRIPE_SECRET_KEY="sk_test_..."  # or sk_live_...
STRIPE_WEBHOOK_SECRET="whsec_..."
STRIPE_PRICE_ID="price_..."
STRIPE_PRODUCT_ID="prod_..."
STRIPE_METER_EVENT="email_sent"
  • STRIPE_SECRET_KEY - Your Stripe API secret key
  • STRIPE_WEBHOOK_SECRET - Webhook signing secret for verifying events
  • STRIPE_PRICE_ID - Usage-based price ID from your Stripe product
  • STRIPE_PRODUCT_ID - Product ID from Stripe
  • STRIPE_METER_EVENT - Name of the billing meter event

Optional Variables

Redis

REDIS_HOST="localhost"
Redis host for caching (optional but recommended for production).

Rupt Integration

RUPT_SECRET_KEY="your_secret_key"
Optional secret key for Rupt integration (fake accounts & account takeover features).

Port Configuration

PORT="8006"
Port for the API server (defaults to 8006).

Environment

ENV="production"  # or "development"
Environment identifier for logging and debugging.

Running Locally

API Server

Sendook uses Bun instead of Node.js for better performance. Development Mode
cd api
bun install
bun dev
The API will start on http://localhost:8006 with hot reload enabled. Production Mode
cd api
bun install
bun start
Bun automatically loads .env files, so you don’t need dotenv.

Dashboard App

The dashboard is built with Nuxt 3. Development Mode
cd app
bun install
bun dev
Production Mode
cd app
bun install
bun build
bun start
Environment Variables
API_URL="http://localhost:8006"  # Point to your API

Landing Site

The marketing site is also built with Nuxt 3.
cd landing
bun install
bun dev  # Development
bun run build && bun start  # Production
No environment variables required for the landing site.

AWS SES Setup

Sendook uses AWS SES for email sending and receiving.

Prerequisites

  1. AWS Account - Sign up at aws.amazon.com
  2. SES Access - Enable SES in your AWS region
  3. Move out of sandbox - Request production access to send to any email

Configuration Steps

1. Verify Domain In AWS SES Console:
  • Go to “Verified identities”
  • Click “Create identity”
  • Select “Domain”
  • Enter your domain
  • Follow DNS verification steps
2. Create IAM User Create an IAM user with these permissions:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ses:SendEmail",
        "ses:SendRawEmail",
        "ses:VerifyDomainIdentity",
        "ses:GetIdentityVerificationAttributes",
        "ses:VerifyDomainDkim",
        "ses:GetIdentityDkimAttributes"
      ],
      "Resource": "*"
    }
  ]
}
Save the access key ID and secret access key. 3. Configure Receiving For receiving emails:
  • Create an S3 bucket (optional, for email storage)
  • Set up SES receipt rules
  • Configure SNS topics or Lambda functions for processing
4. DNS Records Add these DNS records for each domain: MX Record (for receiving):
Type: MX
Name: @
Value: inbound-smtp.us-east-1.amazonaws.com
Priority: 10
DKIM Records (provided by SES):
Type: CNAME
Name: [subdomain]._domainkey
Value: [value].dkim.amazonses.com
SPF Record (optional but recommended):
Type: TXT
Name: @
Value: v=spf1 include:amazonses.com ~all
DMARC Record (optional):
Type: TXT
Name: _dmarc
Value: v=DMARC1; p=quarantine; rua=mailto:[email protected]

MongoDB Setup

Local MongoDB

Using Docker
docker run -d \
  --name sendook-mongodb \
  -p 27017:27017 \
  -v sendook-data:/data/db \
  mongo:latest
Using MongoDB Community Edition Download and install from mongodb.com.

Hosted MongoDB

Use a managed MongoDB service: Get your connection string and set it as MONGO_URI.

Database Migrations

Sendook runs migrations automatically on startup:
startMongo().then(async () => {
  console.log("MongoDB connected, running migrations...");
  await runMigrations();
  // Start server...
});
Migrations are located in api/migrations/.

Production Deployment

Using Docker Compose

Create a docker-compose.yml:
version: '3.8'

services:
  api:
    build: ./api
    ports:
      - "8006:8006"
    environment:
      MONGO_URI: mongodb://mongo:27017/sendook
      DEFAULT_EMAIL_DOMAIN: sendook.com
      AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
      AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
      STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
      STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET}
      STRIPE_PRICE_ID: ${STRIPE_PRICE_ID}
      STRIPE_PRODUCT_ID: ${STRIPE_PRODUCT_ID}
      STRIPE_METER_EVENT: email_sent
      REDIS_HOST: redis
    depends_on:
      - mongo
      - redis

  app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      API_URL: http://api:8006
    depends_on:
      - api

  mongo:
    image: mongo:latest
    volumes:
      - mongo-data:/data/db
    ports:
      - "27017:27017"

  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

volumes:
  mongo-data:
Run with:
docker-compose up -d

Deployment Platforms

Railway

Deploy with one click using Railway’s Docker support. Connect your GitHub repo and set environment variables.

Fly.io

Use Fly.io for global edge deployment. Create a fly.toml and deploy with fly deploy.

AWS ECS

Run on AWS ECS with Fargate for serverless containers. Use ECR for Docker images.

DigitalOcean

Deploy to DigitalOcean App Platform or Droplets with Docker.

Repository Structure

Understanding the monorepo layout:
sendook/
├── api/                 # Backend API server
│   ├── controllers/     # Business logic
│   ├── db/             # Database schemas
│   ├── models/         # TypeScript interfaces
│   ├── routes/         # API routes
│   ├── migrations/     # Database migrations
│   ├── Dockerfile      # Docker configuration
│   └── index.ts        # Entry point
├── app/                # Dashboard application
│   ├── app/
│   │   ├── components/ # Vue components
│   │   ├── pages/      # Dashboard pages
│   │   └── composables/ # Vue composables
│   └── nuxt.config.ts  # Nuxt configuration
├── landing/            # Marketing website
│   └── nuxt.config.ts
└── node-sdk/           # Official Node.js SDK
    └── src/

Security Considerations

Always use environment variables for secrets. Never commit credentials to version control.

Production Checklist

  • Use strong, unique passwords for MongoDB
  • Enable MongoDB authentication
  • Use Stripe live mode keys (not test keys)
  • Configure HTTPS/TLS for all endpoints
  • Set up firewall rules (only allow necessary ports)
  • Enable AWS SES production access (out of sandbox)
  • Rotate AWS and Stripe keys regularly
  • Set up monitoring and logging
  • Configure backup strategy for MongoDB
  • Use Redis for session management
  • Set up rate limiting
  • Enable CORS only for trusted origins

Monitoring and Logs

Health Check Endpoint

The API provides a health check:
curl http://localhost:8006/health
Response:
{
  "healthy": true,
  "environment": "production"
}

Logging

Sendook logs to stdout/stderr. Capture logs with: Docker
docker logs sendook-api
Production Use a logging service:
  • CloudWatch Logs (AWS)
  • Datadog
  • Loggly
  • Papertrail

Troubleshooting

  • Verify MONGO_URI is correct
  • Check MongoDB is running: docker ps or systemctl status mongod
  • Ensure network connectivity between API and MongoDB
  • Check MongoDB authentication credentials
  • Verify AWS credentials are correct
  • Check SES is enabled in your AWS region
  • Ensure you’re out of SES sandbox mode
  • Verify domain is verified in SES
  • Check CloudWatch logs for SES errors
  • Verify all Stripe environment variables are set
  • Check you’re using the correct key type (test vs live)
  • Ensure webhook secret matches your Stripe webhook
  • Verify the price ID exists in your Stripe account
  • Change PORT environment variable to an available port
  • Check for other processes: lsof -i :8006
  • Stop conflicting services
  • Ensure Docker has enough memory allocated
  • Check package.json and bun.lock are present
  • Verify Docker has internet access for dependencies
  • Try docker system prune to clear cache

Performance Optimization

Use Redis

Configure Redis for session caching and improved performance.

Database Indexing

MongoDB indexes are created by migrations. Monitor slow queries.

Connection Pooling

Mongoose handles connection pooling automatically.

Load Balancing

Deploy multiple API instances behind a load balancer for high availability.

Next Steps

Authentication

Set up API keys and access tokens

Dashboard

Learn how to use the dashboard

API Reference

Explore the API endpoints

Billing Setup

Configure Stripe billing

Build docs developers (and LLMs) love