Case study · DevOps
SunBot → AWS, as code
I took a live production Discord bot (35+ servers, 90k+ member reach) and rebuilt its
environment as a reproducible AWS stack defined entirely in Terraform — provisioned
from scratch with a single terraform apply.
Terraform
AWS
Docker
Linux
The problem
- The bot ran on managed hosting — one-click, but a black box you can't reproduce or review.
- No infrastructure as code: nothing described, versioned, or repeatable.
- Backups lived on the same host as the bot — a single point of failure.
- MySQL shared one tiny instance with the app.
Approach
Built incrementally — every step a working terraform apply I could inspect and tear down, so nothing was a big-bang risk:
- S3 bucket for off-site backups (versioned, private, auto-expiring) — safety first.
- EC2 + security group (SSH locked to my IP) + SSH key pair.
- Containerized deploy via Docker Compose; code pulled with a read-only deploy key.
- RDS managed MySQL, reachable only from the app's security group (SG-to-SG, never public).
- AWS Budgets cost guardrail with email alerts.
Architecture
AWS eu-central-1 (default VPC)
you ──SSH(22)──▶ EC2 t3.micro ──MySQL(3306)──▶ RDS MySQL
(Docker: bot) private, SG-locked
│
└── off-site dumps ──▶ S3 (versioned)
AWS Budgets watches spend · all defined in Terraform
Decisions & lessons
- Private database: RDS accepts traffic only from the EC2's security group — no public endpoint, no IP allow-listing to maintain.
- Guardrails before compute: set the cost budget first, so spinning up RDS/Lambda couldn't surprise me with a bill.
- Real debugging: the 1 GB instance OOM-locked under MySQL + build load; diagnosed it and added swap, then moved the DB off the box onto RDS entirely.
- Honest scope: this is a reproducible, demonstrated deployment; production still runs on the managed host — the value here is the IaC, not a risky cut-over of a live user-facing product.
What's next
Kubernetes (kind) for orchestration and deeper Grafana/Prometheus dashboards-as-code — extending the same repo.