useSendEverJust

Architecture (AWS)

How the EverJust useSend instance runs on AWS — VPC, ECS Fargate, RDS, Redis, ALB, SES, and the end-to-end send and event path.

This page describes how the single EverJust useSend instance is deployed on AWS. Everything below is provisioned by Terraform in the terraform/ directory of github.com/EVERJUST-DEV/usesend-email — the diagram and the resource list are the code, not a whiteboard sketch.

One AWS deployment serves every product. Tenancy is logical: a Team is a tenant, with its own sending domains and API keys. See Tenant isolation for how Teams keep products apart on shared infrastructure.

Topology

                              Internet

                    ┌────────────┼────────────────────────┐
                    │            │ (443, mail.everjust.app)│
                    │            ▼                         │
                    │   ┌──────────────────┐               │
                    │   │  ALB + ACM (TLS) │  Route 53 DNS │
                    │   └────────┬─────────┘               │
                    │            │ SG: only ALB → task     │
   VPC (2 AZs)      │            ▼                         │
   ┌────────────────┼──────────────────────────────────┐  │
   │ PUBLIC subnets │                                   │  │
   │  ┌─────────────▼──────────────────────────────┐   │  │
   │  │ ECS Fargate — 1 useSend container          │   │  │
   │  │  web server + in-process BullMQ workers    │───┼──┼──► SES API
   │  │  public IP (egress; no NAT gateway)        │   │  │   (send)
   │  └───────┬───────────────────────┬────────────┘   │  │
   │          │                       │                │  │
   │ PRIVATE subnets                  │                │  │
   │  ┌───────▼────────┐   ┌──────────▼───────────┐    │  │
   │  │ RDS Postgres   │   │ ElastiCache Redis     │    │  │
   │  │ (application   │   │ (BullMQ queue;        │    │  │
   │  │  data)         │   │  maxmemory-policy     │    │  │
   │  └────────────────┘   │  noeviction)          │    │  │
   │                       └──────────────────────┘    │  │
   └───────────────────────────────────────────────────┘  │
                    │            ▲                         │
                    │ S3 assets  │ SES delivery events     │
                    └────────────┼─────────────────────────┘

                          SNS topic ──► POST /api/ses_callback
                                        (updates useSend logs)

Components

LayerAWS servicePlacementPurpose
ComputeECS Fargate (1 task)Public subnetsSingle useSend container: HTTP server + in-process BullMQ workers
IngressALB + ACM certificateTLS termination for mail.everjust.app; only path to the task
DatabaseRDS PostgresPrivate subnetsAll application/tenant data
QueueElastiCache RedisPrivate subnetsBullMQ job queue; maxmemory-policy noeviction
AssetsS3Static assets
DNSRoute 53mail.everjust.app and domain records
DeliveryAmazon SESSends outbound mail; emits delivery events
EventsSNS topicDelivers SES notifications to /api/ses_callback

Networking

The VPC spans two Availability Zones. The Fargate task runs in public subnets with a public IP, which it uses for outbound egress (to the SES API, S3, and other AWS endpoints).

There is no NAT gateway. The AWS account had hit its Elastic IP cap, so the NAT gateway was dropped and the task egresses directly via its own public IP. This does not open the task to the internet: a security group restricts inbound traffic to the ALB only, so the container is not reachable except through mail.everjust.app. RDS and Redis stay in private subnets and are never internet-reachable.

Send path

The application talks to the SES API directly; SES delivers to the recipient.

useSend container  ──►  Amazon SES API  ──►  recipient mailbox

Outbound sends are enqueued in Redis (BullMQ) and processed by the workers running inside the same container, then handed to SES.

Event path

SES reports delivery outcomes back into useSend so message logs reflect real status:

Amazon SES  ──►  SNS topic  ──►  POST /api/ses_callback  ──►  useSend updates the message log

This was verified end-to-end: a test send transitioned SENT → DELIVERED in the useSend logs, confirming the SES → SNS → callback loop is wired correctly.

SES is currently in sandbox. Until AWS grants production access (requested, pending), sends are limited to verified addresses and the SES mailbox simulator (success@simulator.amazonses.com, bounce@, complaint@). After production access, any recipient is allowed. Rate limiting is disabled on this self-hosted instance — throughput is bounded only by SES sending limits.

Cost

Baseline infrastructure runs roughly $65–80/month (Fargate task, ALB, RDS, ElastiCache, and supporting resources), plus Amazon SES per-email charges on top. Cost scales primarily with SES send volume rather than with the number of Teams.

Reproducibility

Every resource above is defined as Terraform in terraform/ in the usesend-email repo. Infrastructure changes go through Terraform, not the console.