Unosend V2 Architecture
Production-ready, scalable email infrastructure with regional queues and Docker workers.
Overview
┌─────────────────────────────────────────────────────────────────────────────┐ │ VERCEL API LAYER │ │ │ │ 1. Receive API request │ │ 2. Validate API key via Redis cache (NOT Supabase on every request) │ │ 3. Check if user is PAID │ │ 4. Get user IP → determine region (asia/eu/us) │ │ 5. Check queue depths across ALL regions │ │ 6. Route to best queue (prefer user region, fallback if overloaded) │ │ 7. Send email data to selected regional queue │ └─────────────────────────────────────────────────────────────────────────────┘ │ ┌────────────────┼────────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ ASIA-REDIS│ │ EU-REDIS │ │ US-REDIS │ │ (Mumbai) │ │ (Frankfurt)│ │ (Virginia)│ │ │ │ │ │ │ │ Contabo │ │ OVH │ │ Future │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ WORKER │ │ WORKER │ │ WORKER │ │ REPLICAS │ │ REPLICAS │ │ REPLICAS │ │ (Docker) │ │ (Docker) │ │ (Docker) │ │ │ │ │ │ │ │ 3 replicas│ │ 3 replicas│ │ 3 replicas│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ KumoMTA │ │ KumoMTA │ │ KumoMTA │ │ (Local) │ │ (Local) │ │ (Local) │ └───────────┘ └───────────┘ └───────────┘Part 1: Redis Cluster (3 Regions)
Infrastructure
| Region | Server | IP | Redis Port | Purpose |
|---|---|---|---|---|
| Asia | Contabo (Mumbai) | 217.217.250.114 | 6379 | asia-queue |
| EU | Contabo (Germany) | 84.247.139.105 | 6379 | eu-queue |
| US | TBD | TBD | 6379 | us-queue |
Redis Configuration
Each Redis instance runs in Docker with:Environment Variables (Vercel)
Part 2: API Key Caching in Redis
Why Cache?
- Supabase query: ~100-200ms
- Redis cache: ~1-5ms
- 20-40x faster validation
Cache Structure
Key: apikey: TTL: 300 seconds (5 minutes) Value: JSONImplementation
Part 3: Queue Router (Vercel)
Logic
- Get user IP → determine region
- Check queue depths across all regions
- If user’s region queue < threshold → use it
- Else → use least loaded queue
Part 4: Queue Payload Structure
What Vercel Sends to Queue
Part 5: Docker Worker
Design Patterns Used
- Worker Pool Pattern - Goroutine pool for concurrent processing
- Circuit Breaker - Prevent cascade failures to KumoMTA
- Graceful Shutdown - Handle SIGTERM properly
- Health Checks - Kubernetes/Docker health endpoints
- Structured Logging - JSON logs for observability
- Metrics - Prometheus metrics for monitoring
Dockerfile
Worker Code Structure
docker/worker/ ├── Dockerfile ├── go.mod ├── go.sum ├── cmd/ │ └── worker/ │ └── main.go # Entry point ├── internal/ │ ├── config/ │ │ └── config.go # Environment config │ ├── queue/ │ │ └── consumer.go # Redis queue consumer │ ├── processor/ │ │ └── email.go # Email processing logic │ ├── validator/ │ │ ├── mx.go # MX record validation │ │ └── suppression.go # Suppression list check │ ├── dkim/ │ │ └── signer.go # DKIM signing │ ├── smtp/ │ │ └── client.go # KumoMTA SMTP client │ ├── metrics/ │ │ └── prometheus.go # Metrics │ └── health/ │ └── handler.go # Health check endpoint └── pkg/ └── models/ └── payload.go # Shared data structuresMain Worker Code
Email Processor
Part 6: Docker Compose (Per Server)
Part 7: Deployment
GitHub Actions CI/CD
Deploy to Servers
Part 8: Monitoring
Prometheus Metrics
Grafana Dashboard
- Emails processed/minute per region
- Queue depth per region
- Processing latency p50/p95/p99
- Error rate by type
- Worker health status
Implementation Order
-
Phase 1: Redis Cluster (Day 1)
- Set up Redis on both servers
- Configure authentication
- Test connectivity from Vercel
-
Phase 2: API Key Caching (Day 1)
- Implement Redis cache in Vercel
- Add cache invalidation
-
Phase 3: Queue Router (Day 2)
- Implement region detection
- Implement queue depth checking
- Implement smart routing
-
Phase 4: Docker Worker (Day 2-3)
- Build Go worker with goroutines
- Dockerize
- Test locally
-
Phase 5: Deployment (Day 3)
- Push to GHCR
- Deploy to servers
- Test end-to-end
-
Phase 6: Monitoring (Day 4)
- Add Prometheus metrics
- Set up Grafana
- Create alerts
Environment Variables Summary
Vercel
Docker Worker (per server)
Notes
- Each server runs: Redis + Worker replicas + KumoMTA
- Workers connect to local Redis only
- Vercel routes to correct regional Redis
- DKIM keys fetched from Supabase (cached in worker)
- Return-Path rewritten for bounce tracking
- Graceful shutdown on SIGTERM
- Health checks for container orchestration