Serverless Wasn't Always the Answer
Why I Moved from ECS Fargate Back to EC2 + Nginx + Docker Compose
When I first designed the infrastructure, I picked the "newest, most-recommended" architecture I could find. Then, a few days before demo day, I tore it down myself. This is the story of that decision.
What I Was Building (Identity)
I'm a Computer Science student at Dongduk Women's University, and I owned the backend and infrastructure for Proovy, a personal AI tutor for STEM students. I learned something this project taught me the hard way: writing the code is far easier than shipping something you can actually operate.
That's also how I fell into cloud computing. At first I thought, "Don't you just upload it to a server?" But once I actually ran a service in production, I realized that something that runs and something that's sustainable are two completely different problems.
1. The First Architecture: Serverless Containers
The initial setup looked like this:
1
CloudFront → ALB → ECS FargateMy reasons for choosing a serverless container architecture on ECS Fargate were clear:
- Less server management — no need to patch and maintain EC2 instances myself
- Clean per-service scaling — scale at the container level
On paper, it was about as close to the "model answer" as you can get. So I went with it without a second thought.
2. Looking at It Through an Operations Lens, the Cracks Showed (Community & Learning)
Once I analyzed our demo environment and the conditions of an early-stage service, two problems surfaced.
Problem #1: For an always-on API server, serverless is actually expensive
Fargate doesn't bill per request — it bills by task runtime × vCPU/memory. Which means:
- Even with zero traffic, you keep paying as long as the API server is alive.
- Running a 24/7 API creates a fixed cost that has nothing to do with traffic.
The whole serverless promise — "pay only for what you use" — didn't fit the nature of our service, which is an API server that has to stay on all the time.
💡 I'd learned this lesson more expensively on another project (SomShare, a study-archive sharing service). Back then I treated infra setup as an afterthought, got billed $60 in a single day, and learned — through the invoice — that infrastructure being sustainable matters more than infrastructure merely running. On Proovy, I didn't want to repeat that mistake.
Problem #2: On demo day, "the ability to intervene" is stability
Two things scared me most about demo day:
- Sudden traffic spikes
- The unstable network conditions of a live venue
Fargate's high level of abstraction made it hard to see inside and intervene when something went wrong. Normally that abstraction is a feature — but in a "the demo has to work, on stage, right now" moment, I decided that an architecture I could put my hands directly into was the safer bet.

3. The Architecture I Switched To (Implementation)
So I moved to this:
1
CloudFront → ALB → EC2 (Auto Scaling Group) → Nginx → Docker Compose| Layer | Role | Why I changed it |
|---|---|---|
| CloudFront | Static caching / edge | Kept as-is |
| ALB | Load balancing | Kept as-is |
| EC2 (ASG) | Compute | Predictable fixed cost for an always-on API, and I can intervene directly |
| Nginx | Reverse proxy | I control routing, timeouts, etc. myself |
| Docker Compose | Container orchestration | Simple multi-container management inside a single instance |
The key point: I didn't abandon serverless — I re-chose the level of abstraction that fit this service's operating conditions. Fargate wasn't wrong. It just wasn't the right fit for a demo-stage service that has to be always on, needs hands-on intervention, and demands predictable cost.
4. What I Learned (Lessons Learned)
- "Newest architecture" ≠ "right answer for my service." Match the billing model (per-request vs. per-runtime) against your traffic pattern (always-on vs. bursty) before you commit.
- Abstraction is a double-edged sword. Higher abstraction is more convenient, but it shrinks your room to intervene when things break. Choose based on "when will I need to get in there myself?"
- Cost is decided at design time. Don't wait for the invoice to regret it. (This one was genuinely a $60 lesson.)
What's Next (Passion & Purpose)
I'm now an AWS Cloud Club Captain, and I run sessions where I draw out network and infrastructure architectures with newer members. The one thing this project burned into me: a "good architecture" isn't the prettiest diagram — it's what you get when you honestly confront your service's operating conditions.
If I were advising a peer designing their first infrastructure, I'd say:
Don't start by picking the coolest structure. First answer three questions: "When does this server need to be on? Who sends traffic, and how? When will I need to step in?" The architecture follows from those answers.
In my next article, I'll break down — in the same way — how I solved the cost and load problems of large-file downloads on SomShare using S3 + Presigned URLs and public/private subnet isolation.
Thanks for reading. If you're weighing a similar migration, drop your service's conditions in the comments — let's reason through it together.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article