API server that acts as Mailgun server to proxy emails to SES
This API server acts as a proxy to enable sending newsletter emails from Ghost using Amazon SES (Simple Email Service) instead of Mailgun. It mimics the Mailgun API endpoints while routing the actual email-sending logic through SES on the backend.
Ghost natively integrates with Mailgun for sending newsletters. This proxy allows you to continue using Ghost's Mailgun integration while leveraging the cost-effectiveness and reliability of Amazon SES.
- Mailgun API Compatibility: Mimics Mailgun's v3 API endpoints for seamless Ghost integration
- Amazon SES Backend: Routes all email sending through AWS SES for better deliverability and cost-effectiveness
- Queue-based Processing: Uses AWS SQS for reliable email queue management
- Event Tracking: Comprehensive email event tracking (delivery, bounce, complaint, etc.)
- Database Logging: Stores email batches, messages, and events in MySQL database
- Health Monitoring: Built-in health check endpoints for monitoring
- Docker Support: Containerized deployment with Docker Compose
The system consists of several components:
- Next.js API Server: Handles incoming requests from Ghost
- AWS SES: Sends the actual emails
- AWS SQS: Manages email queues and event notifications
- MySQL Database: Stores email batches, messages, and delivery events
- Background Processors: Process email queues and handle SES events
Before setting up the server, ensure you have:
- Node.js (v18 or higher)
- MySQL database
- AWS Account with SES and SQS access
- Docker (optional, for containerized deployment)
- Verify your sending domain in AWS SES console
- Create Configuration Sets for tracking:
newsletter-configuration-set(for newsletter emails)system-configuration-set(for transactional emails)
- Set up SNS topics for event notifications (optional but recommended)
- Request production access if sending to unverified email addresses
Create the following SQS queues:
newsletter-queue- For processing newsletter emailsnewsletter-notification-queue- For SES event notificationssystem-notification-queue- For transactional email notifications
Your AWS credentials need the following permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail",
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
],
"Resource": "*"
}
]
}Create a .env file in the project root with the following variables:
# AWS Configuration
AWS_ACCESS_KEY_ID=your_aws_access_key
AWS_SECRET_ACCESS_KEY=your_aws_secret_key
# Database Configuration
DATABASE_URL="mysql://username:password@localhost:3306/mailgun_ses_db"
# SES Configuration
SES_REGION="us-east-1"
SES_TRANSACTIONAL_REGION="us-east-1"
TRANSACTIONAL_CONFIGURATION_SET_NAME=system-configuration-set
NEWSLETTER_CONFIGURATION_SET_NAME=newsletter-configuration-set
# SQS Configuration
SQS_REGION="us-east-1"
NEWSLETTER_QUEUE="https://sqs.us-east-1.amazonaws.com/123456789012/newsletter-queue"
NEWSLETTER_NOTIFICATION_QUEUE="https://sqs.us-east-1.amazonaws.com/123456789012/newsletter-notification-queue"
TRANSACTIONAL_NOTIFICATION_QUEUE="https://sqs.us-east-1.amazonaws.com/123456789012/system-notification-queue"
# Email Configuration
SYSTEM_FROM_ADDRESS="Your App <[email protected]>"
# API Security
API_KEY="your-secure-api-key-here"
# Server Configuration (optional)
PORT=3000
NODE_ENV=production-
Clone the repository
git clone <repository-url> cd mailgun-to-ses-proxy
-
Install dependencies
npm install
-
Set up the database
# Generate Prisma client npm run db:generate # Run database migrations npm run db:migrate:dev
-
Start the development server
npm run dev
-
Using Docker Compose (includes MySQL)
docker-compose up -d
-
Using standalone Docker
# Build the image docker build -t mailgun-ses-proxy . # Run the container docker run -p 3000:3000 --env-file .env mailgun-ses-proxy
-
Build the application
npm run build
-
Start the production server
npm start
Configure Ghost to use the proxy by setting these environment variables in your Ghost installation:
# Mailgun Configuration (point to your proxy)
bulkEmail__mailgun__baseUrl=http://your-proxy-server:3000/v3
bulkEmail__mailgun__apiKey=your-secure-api-key-here
bulkEmail__mailgun__domain=your-verified-ses-domain.com
# Email Settings
hostSettings__managedEmail__sendingDomain=your-verified-ses-domain.com
[email protected]POST /v3/{siteId}/messages- Send newsletter emails (Mailgun compatible)GET /healthcheck- Health check endpointGET /stats/{action}- Email statistics and analytics
The proxy supports the following Mailgun parameters:
from- Sender email addressto- Recipient email address(es)subject- Email subjecthtml- HTML email contenttext- Plain text email contentv:email-id- Batch ID for tracking
Monitor your deployment using the health check endpoint:
curl http://your-server:3000/healthcheckThe application uses structured logging with Pino. Logs include:
- Email sending events
- Queue processing status
- Error tracking
- Performance metrics
Monitor email delivery through the database tables:
NewsletterBatch- Email batch informationNewsletterMessages- Individual email messagesNewsletterErrors- Failed email attemptsNewsletterNotifications- SES delivery events
-
SES Sandbox Mode
- Ensure you've requested production access in AWS SES
- Verify all recipient domains in sandbox mode
-
Queue Processing Issues
- Check SQS queue visibility timeout settings
- Verify AWS credentials and permissions
- Monitor dead letter queues for failed messages
-
Database Connection
- Ensure MySQL is running and accessible
- Verify DATABASE_URL format and credentials
- Check if migrations have been applied
-
Ghost Integration
- Verify the proxy URL is accessible from Ghost
- Check API key matches between Ghost and proxy
- Ensure the domain is verified in SES
Enable debug logging by setting:
NODE_ENV=developmentTest the proxy directly:
curl -X POST http://localhost:3000/v3/your-site-id/messages \
-H "Authorization: Bearer your-api-key" \
-F "[email protected]" \
-F "[email protected]" \
-F "subject=Test Email" \
-F "html=<h1>Test Message</h1>"- Queue Processing: The system processes emails asynchronously through SQS
- Rate Limits: Respects AWS SES sending limits automatically
- Batch Processing: Handles large newsletter batches efficiently
- Error Handling: Implements retry logic for failed deliveries
- Use strong API keys for authentication
- Implement proper IAM roles with minimal required permissions
- Keep AWS credentials secure and rotate regularly
- Use HTTPS in production deployments
- Regularly update dependencies for security patches
The Mailgun-to-SES proxy is currently being used in production at typetale.app for sending both newsletter and transactional emails. It has proven to be a stable and scalable solution that meets all the service requirements.
AGPL-3