EC2 + Caddy Cost-Optimized Pipeline — ~$7.5/mo
costing.climacs.net ↗
| Component | V1 Enterprise | V2 EC2 | Monthly Savings |
|---|---|---|---|
| Compute | ECS Fargate (0.25 vCPU) $9.00/mo |
EC2 t4g.nano $3.07/mo |
-$5.93 |
| Load Balancer | ALB $16.20/mo |
Caddy (on host) $0.00 |
-$16.20 |
| TLS Certificate | ACM (free) | Let's Encrypt (free) | $0.00 |
| Static IP | ALB DNS (included) | Elastic IP $3.65/mo |
+$3.65 |
| Route53 | Alias → ALB | A record → EIP | $0.00 |
| Lambda + Athena | rate(15 min) | rate(8 hours) | ~93 fewer runs/day |
| Deployment | ECS service update | SSM RunCommand | — |
| Total Monthly | ~$28/mo | ~$7.5/mo | -$20.5/mo (73%) |
AWS Graviton2 ARM64 processor. 2 vCPUs, 0.5 GiB memory. Runs Amazon Linux 2023.
Docker installed via cloud-init user_data.sh. Pulls the FastAPI backend
container from ECR on first boot. Instance profile grants S3 read-only access.
Automatic HTTPS reverse proxy. Caddy handles TLS certificate provisioning and renewal
from Let's Encrypt. Listens on ports 80 (HTTP redirect) and 443 (HTTPS), proxying
all traffic to the backend container on localhost:8000. Zero-config TLS.
Static IPv4 address assigned to the EC2 instance. Route53 A-record points directly to this IP — no ALB indirection. Caddy uses this IP for Let's Encrypt ACME validation. Note: AWS charges $3.65/mo for EIPs since Feb 2024.
Scheduled rule triggers Lambda aggregator 3×/day (every 8 hours). Reduced from 15-minute intervals in V1 — CUR data only updates 1–3×/day, so higher frequency was pure waste. Saves ~93 Lambda + Athena invocations daily.
Runs 6 Athena SQL queries against CUR Parquet files, aggregating costs by service, day, and tag. Writes JSON results to the S3 Aggregates bucket. Execution time ~3s. Shared between V1 and V2 — no changes needed.
All V2 infrastructure defined in terraform-v2-ec2/. Separate state file
(costing/v2-ec2.tfstate) from V1. EC2, security group, IAM role, EIP,
Route53, Lambda, S3/Athena — all codified. GitLab CI runs on v2-ec2 branch.
/api/* endpoints require credentials. Credentials injected via environment file on EC2. Frontend login modal remains unchanged..gitlab-ci.yml. Pushes trigger validate → plan → apply → build → deploy stages targeting terraform-v2-ec2/.docker buildx to cross-compile for linux/arm64, ensuring compatibility with Graviton2 t4g.nano. Images pushed to ECR with commit SHA + latest tags.RunCommand to trigger a Docker pull + restart on the EC2 instance. No SSH keys needed.costing/v2-ec2.tfstate in the same S3 backend. Completely isolated from V1's state — both can coexist safely during migration.To switch from V1 Enterprise to V2 EC2, deploy V2 first, verify Caddy TLS acquisition, then swing Route53 from ALB alias to EIP A-record. Finally, destroy V1 Fargate resources. Both versions share the same S3 data pipeline — zero data migration needed.
# 1. Deploy V2
cd terraform-v2-ec2 && terraform apply
# 2. Verify HTTPS
curl -I https://costing.climacs.net
# 3. Destroy V1 (after validation)
cd ../terraform && terraform destroy