đ Overview
High-performance email delivery infrastructure for enterprise use
High Performance
Built in Go with concurrent workers, connection pooling, and intelligent rate limiting
Full Authentication
DKIM signing, SPF validation, DMARC compliance out of the box
Real-time Metrics
Prometheus + Grafana dashboards for complete visibility
Docker Ready
One-command deployment with Docker Compose
Key Features
- â Multi-domain support with per-domain DKIM keys
- â IP pool management with rotation strategies
- â Automatic retry with exponential backoff
- â Bounce handling and suppression lists
- â Campaign management with CSV import
- â Variable substitution in templates
- â REST API with API key authentication
- â IP warmup support
đ Requirements
System requirements for running MailForge
| Component | Minimum Version | Recommended |
|---|---|---|
| Operating System | Ubuntu 20.04 / Debian 11 | Ubuntu 22.04 LTS |
| Docker | 20.10+ | 24.0+ |
| Docker Compose | 2.0+ | 2.20+ |
| PostgreSQL | 14+ | 16 |
| Redis | 6+ | 7+ |
| RAM | 2 GB | 4 GB+ |
| CPU | 2 cores | 4+ cores |
đĻ Installation
Step-by-step installation guide
Install Docker & Docker Compose
# Update system
apt update && apt upgrade -y
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install Docker Compose
apt install docker-compose-plugin -y
# Verify installation
docker --version
docker compose version
Install PostgreSQL
# Install PostgreSQL 16
apt install postgresql postgresql-contrib -y
# Start and enable PostgreSQL
systemctl start postgresql
systemctl enable postgresql
# Create database and user
sudo -u postgres psql
CREATE USER mailforge WITH PASSWORD 'mailforge';
CREATE DATABASE mailforge OWNER mailforge;
GRANT ALL PRIVILEGES ON DATABASE mailforge TO mailforge;
\q
# Edit postgresql.conf
nano /etc/postgresql/16/main/postgresql.conf
# Change: listen_addresses = '*'
# Edit pg_hba.conf
nano /etc/postgresql/16/main/pg_hba.conf
# Add: host all all 172.17.0.0/16 md5
# Restart PostgreSQL
systemctl restart postgresql
Install Redis
# Install Redis
apt install redis-server -y
# Configure Redis to listen on all interfaces
sed -i 's/bind 127.0.0.1/bind 0.0.0.0/' /etc/redis/redis.conf
# Start Redis
systemctl restart redis
systemctl enable redis
Clone and Deploy MailForge
# Clone repository
cd /home
Upload your mailforge.zip and unzip it on /home
cd mailforge
# Run database migrations
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -f migrations/001_initial_schema.sql
# Build and start
docker compose build
docker compose up -d
# Check logs
docker compose logs -f
Initialize Database Records
# Create default account
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -c "
INSERT INTO accounts (id, name, email, api_key_hash, created_at, updated_at)
VALUES (
'00000000-0000-0000-0000-000000000001',
'Default',
'[email protected]',
'your_api_key',
NOW(), NOW()
);"
# Create IP pool
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -c "
INSERT INTO ip_pools (id, account_id, name, rotation_strategy, active, created_at, updated_at)
VALUES (
'00000000-0000-0000-0000-000000000001',
'00000000-0000-0000-0000-000000000001',
'Default',
'round-robin',
true, NOW(), NOW()
);"
# Add your server IP
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -c "
INSERT INTO ip_addresses (id, ip_pool_id, ip_address, hostname, active, created_at, updated_at)
VALUES (
'00000000-0000-0000-0000-000000000001',
'00000000-0000-0000-0000-000000000001',
'YOUR_SERVER_IP',
'mail.yourdomain.com',
true, NOW(), NOW()
);"
đ DNS Setup
Configure DNS records for email delivery
Required DNS Records
| Type | Name | Value | Purpose |
|---|---|---|---|
| A | mail.yourdomain.com | YOUR_SERVER_IP | Mail server hostname |
| PTR | YOUR_SERVER_IP | mail.yourdomain.com | Reverse DNS (contact hosting provider) |
| MX | yourdomain.com | 10 mail.yourdomain.com | Mail exchange record |
| TXT | yourdomain.com | v=spf1 ip4:YOUR_SERVER_IP ~all | SPF - Authorizes your IP to send |
| TXT | _dmarc.yourdomain.com | v=DMARC1; p=none; rua=mailto:[email protected] | DMARC policy |
| TXT | mail._domainkey.yourdomain.com | v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY | DKIM public key |
Verify DNS Records
# Check SPF
dig TXT yourdomain.com +short
# Check DKIM
dig TXT mail._domainkey.yourdomain.com +short
# Check DMARC
dig TXT _dmarc.yourdomain.com +short
# Check PTR (reverse DNS)
dig -x YOUR_SERVER_IP +short
đ DKIM Configuration
Generate and configure DKIM keys for email signing
Generate DKIM Key Pair
# Generate 2048-bit RSA key pair
openssl genrsa -out yourdomain.com.private 2048
openssl rsa -in yourdomain.com.private -pubout -out yourdomain.com.public
# View private key (save for database)
cat yourdomain.com.private
# View public key (for DNS record)
cat yourdomain.com.public
Add Domain to Database
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -c "
INSERT INTO domains (id, account_id, domain, dkim_private_key, dkim_selector, dkim_enabled, verified, created_at, updated_at)
VALUES (
'00000000-0000-0000-0000-000000000001',
'00000000-0000-0000-0000-000000000001',
'yourdomain.com',
'-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_HERE
-----END PRIVATE KEY-----',
'mail',
true,
true,
NOW(), NOW()
);"
Add DKIM DNS Record
Create a TXT record with the following format:
Name: mail._domainkey.yourdomain.com
Value: v=DKIM1; k=rsa; p=YOUR_PUBLIC_KEY_BASE64_WITHOUT_HEADERS
đ API Usage
Send emails using the REST API
Authentication
All API requests require an API key in the X-API-Key header.
Send Single Email
curl -X POST http://localhost:8080/api/v1/messages \
-H "Content-Type: application/json" \
-H "X-API-Key: mf_test_key_12345" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Hello World",
"html_body": "<h1>Hello!</h1><p>This is a test email.</p>"
}'
Send Campaign with CSV
#!/bin/bash
API_URL="http://localhost:8080/api/v1"
API_KEY="mf_test_key_12345"
CSV_FILE="contacts.csv"
# Email content
FROM_NAME="Your Company"
FROM_EMAIL="[email protected]"
SUBJECT="Hello {{name}}!"
HTML_BODY="<h1>Hi {{name}}!</h1><p>Thanks for subscribing!</p>"
# DKIM settings
DKIM_DOMAIN="yourdomain.com"
DKIM_SELECTOR="mail"
# Create campaign
CAMPAIGN_ID=$(curl -s -X POST "$API_URL/campaigns" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"name": "My Campaign"}' | jq -r '.id')
echo "Created campaign: $CAMPAIGN_ID"
# Read CSV
CSV_CONTENT=$(cat "$CSV_FILE" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')
# Send campaign
curl -X POST "$API_URL/campaigns/$CAMPAIGN_ID/import" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d "{
\"from_name\": \"$FROM_NAME\",
\"from_email\": \"$FROM_EMAIL\",
\"subject\": \"$SUBJECT\",
\"html_body\": \"$HTML_BODY\",
\"dkim_domain\": \"$DKIM_DOMAIN\",
\"dkim_selector\": \"$DKIM_SELECTOR\",
\"csv\": $CSV_CONTENT
}"
CSV Format
email,name
[email protected],John Doe
[email protected],Jane Smith
[email protected],Bob Wilson
API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/messages | Send single email |
| POST | /api/v1/campaigns | Create campaign |
| POST | /api/v1/campaigns/:id/import | Import CSV and send |
| GET | /api/v1/campaigns/:id/stats | Get campaign statistics |
| POST | /api/v1/suppressions | Add to suppression list |
| GET | /api/v1/suppressions | List suppressions |
| GET | /health | Health check |
| GET | /metrics | Prometheus metrics |
đ Monitoring
Monitor your email delivery with Grafana dashboards
Access Dashboards
Grafana
URL: http://your-server:3000
User: admin
Password: admin
Prometheus
URL: http://your-server:9091
Metrics endpoint: /metrics
Key Metrics
| Metric | Description |
|---|---|
| mailforge_messages_total | Total messages by status (delivered, bounced, deferred) |
| mailforge_queue_size | Current queue size |
| mailforge_workers_active | Number of active workers |
| mailforge_delivery_duration_seconds | Email delivery time histogram |
| mailforge_smtp_connections | Active SMTP connections |
Check Logs
# View all logs
docker compose logs -f
# View only mailforge logs
docker compose logs -f mailforge
# Filter for errors
docker compose logs -f mailforge 2>&1 | grep -i error
# Filter for DKIM
docker compose logs -f mailforge 2>&1 | grep -i dkim
đ§ Troubleshooting
Common issues and solutions
Emails Going to Spam
- 1 Verify SPF record is correct: dig TXT yourdomain.com
- 2 Verify DKIM record is correct: dig TXT mail._domainkey.yourdomain.com
- 3 Verify PTR record matches hostname: dig -x YOUR_IP
- 4 Check if IP is blocklisted: MXToolbox
- 5 Warm up new IP addresses gradually
Connection Refused
# Check if PostgreSQL is listening
netstat -tlnp | grep 5432
# Check if Redis is listening
netstat -tlnp | grep 6379
# Test PostgreSQL connection from Docker
docker run --rm postgres:16 psql -h host.docker.internal -U mailforge -d mailforge -c "SELECT 1"
No IP Available
# Check if IP addresses exist in database
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -c "SELECT * FROM ip_addresses;"
# Check if IP pool exists
PGPASSWORD=mailforge psql -U mailforge -h localhost -d mailforge -c "SELECT * FROM ip_pools;"