AWS EC2 Deployment
osqueue deploys to a single EC2 t2.micro instance (AWS free tier eligible) running all services in a Docker container.
Architecture
Internet
│
▼
┌────────────────┐
│ CloudFront │◀── HTTPS (ACM cert)
│ CDN + TLS │
└───────┬────────┘
│ HTTP
▼
┌─────────────────────────────────────────────────┐
│ EC2 t2.micro │
│ │
│ ┌──────────┐ │
│ │ Caddy │◀── HTTP :80 ── CloudFront │
│ │ :80 │ │
│ └────┬─────┘ │
│ │ │
│ ├── yourdomain.com → Docs (static files) │
│ ├── demo.yourdomain.com → Dashboard :3001 │
│ └── api.yourdomain.com → Broker :8080 │
│ │
│ ┌───────────┐ ┌───────────┐ │
│ │ Broker │ │ Broker │ (standby) │
│ │ :8080 │ │ :8081 │ │
│ └───────────┘ └───────────┘ │
│ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │Worker 1│ │Worker 2│ │Worker 3│ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ ┌───────────────┐ │
│ │ Web Dashboard │ │
│ │ :3001 │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────┘
│
▼
┌──────────┐
│ S3 Bucket│
│queue.json│
└──────────┘
Deploy with SST
sst deploy --stage production
This provisions:
- An S3 bucket for queue state
- An ECR repository for the Docker image
- A t2.micro EC2 instance with an Elastic IP
- A CloudFront distribution for TLS termination and CDN
- An ACM certificate for your domain (wildcard)
- A Route53 hosted zone with DNS records
- IAM roles with S3 and ECR access
- A security group allowing HTTP (port 80 only — CloudFront terminates TLS)
The Docker image is built from infra/Dockerfile.ec2 and pushed to ECR.
What the Docker Image Contains
The Dockerfile.ec2 builds a multi-stage image:
- Dependencies: Installs all workspace packages via
bun install - Build: Compiles all packages in dependency order, plus the web dashboard
- Caddy: Copies the Caddy binary for HTTP reverse proxy (TLS is handled by CloudFront)
- Entrypoint: Runs
infra/entrypoint.sh
Entrypoint: What Runs
The entrypoint.sh script starts all services:
- Caddy — HTTP reverse proxy routing by domain (TLS is handled by CloudFront)
- Two brokers on ports 8080 and 8081 — one becomes leader, the other retries every 10 seconds
- Health check wait — waits for at least one broker to become healthy
- Three workers — connect to the leader broker
- Web dashboard — serves the production build on port 3001
Domain Setup
SST automatically provisions Route53 DNS records pointing to the CloudFront distribution. The domain, demo. subdomain, and api. subdomain all route through CloudFront for TLS termination, then to the EC2 instance via an origin. subdomain pointing to the Elastic IP.
If you're using a domain registered outside Route53, update your registrar's nameservers to point to the Route53 hosted zone NS records (shown in SST output).
The Caddyfile routes traffic on the EC2 instance:
yourdomain.com→ documentation site (static files from Docusaurus build)demo.yourdomain.com→ web dashboard (port 3001)api.yourdomain.com→ broker API (port 8080)
Production Environment
The SST config sets these environment variables on the EC2 instance:
DOMAIN=osqueue.com
STORAGE_BACKEND=s3
S3_BUCKET=<auto-created bucket>
S3_REGION=<your AWS region>
GROUP_COMMIT_INTERVAL_MS=2000
BROKER_HEARTBEAT_INTERVAL_MS=30000
S3_MAX_WRITES_PER_MINUTE=30
S3_MAX_READS_PER_MINUTE=60
Cost Breakdown
| Resource | Monthly Cost |
|---|---|
| EC2 t2.micro | Free (750 hrs/month for 12 months) |
| Elastic IP | Free (when attached to running instance) |
| S3 storage | ~$0.01 (single small JSON file) |
| S3 API calls | ~$0.25 (with throttling) |
| Data transfer | ~$0.00 (minimal) |
| Total | ~$0.26/month (free tier) or ~$8.50/month (after free tier) |