The Service, Decided — No. 2

Take one ordinary piece of code — an API handler, a report generator, a worker that processes files as they arrive. On AWS there are three unremarkable ways to run it, and most teams have run all three at different times, often for the same code, often without ever quite deciding to. The code doesn't change. Everything around it does: who patches the machine, what an idle hour costs, what happens at a hundred times the traffic.

Lambda, Fargate, and EC2 are usually presented as a ladder — serverless at the top, servers at the bottom, progress flowing one way. That framing is the first mistake. They are not generations of the same idea. They are three different answers to the same question: how much of the machine do you want to own?

Same code, three owners — ask how much of the machine you want, and the service follows.

Lambda: rent a moment

Lambda is the most radical of the three because it removes the machine from the conversation entirely. You hand AWS a function; it runs when an event calls it, for as long as the work takes — up to a hard ceiling of fifteen minutes for a standard function — and then it is gone. You pay per request and per slice of time, in proportion to the memory you allocated. At zero traffic the bill is zero. There is nothing to patch because, from your side of the glass, there is nothing there.

That makes Lambda the right shape whenever the work is an event with a short life: a webhook, a file landing in a bucket, an API call that finishes in seconds, a schedule that fires twice a day. The decision it forces is about limits, and they are architectural, not configurational. Fifteen minutes is a wall, not a guideline — a job that might run twenty minutes does not belong here, and no amount of tuning changes that. (The newer Lambda Managed Instances variant stretches the ceiling to ninety minutes for asynchronous and event-source invocations; that is a different shape with its own trade-offs, not a loosening of the standard wall.) Cold starts tax latency-sensitive paths. Concurrency is a quota you will meet during the spike you built the thing for. And the cost shape inverts at volume: the per-request pricing that is nearly free at a thousand calls a day becomes the expensive option when the function never stops running.

The trap is treating Lambda as the default for everything new. A function is easy to start and hard to leave: the event sources, the IAM shape, the packaging all assume the model. Serverless is not the absence of a server decision. It is a server decision made once, early, by whoever wrote the first handler.

Fargate: rent a container's worth of machine

Fargate sits in the middle, and the middle is more honest than it gets credit for. You package your code as a container; ECS (or EKS) runs it as a task on capacity AWS manages. There are no instances to patch, size, or rebalance — but there is a task definition, and in it you choose vCPU and memory the way you once chose an instance type. You pay for that allocation for as long as the task runs, whether the code is busy or waiting.

That makes Fargate the right shape for work that outlives a moment but doesn't deserve a machine: long-running services with steady shape, batch jobs that take an hour, workers that need a real filesystem, a specific runtime, or a container you already have. The decision it forces is sizing. Allocate generously and you have rebuilt the EC2 cost problem without the EC2 control; allocate tightly and the task throttles or dies under the load it was hired for. Fargate removes the fleet. It does not remove capacity planning — it moves it into a JSON file where fewer people think to look for it.

The trap is the phrase "no ops." You still own the image, which means you own its patching, its base layers, and its vulnerabilities. Fargate's bargain is narrower and better than the marketing: AWS owns the servers, you own everything you shipped.

EC2: own the machine

EC2 is the original answer and still the only complete one. You choose the instance — general purpose, compute, memory, GPU — the operating system, the storage, the network placement, the tenancy. Everything is yours, including the consequences: patching, hardening, scaling, recovery, and a bill that runs for every hour the instance exists, whether it serves one request or a million.

That makes EC2 the right shape when the work has a requirement the other two cannot meet: a GPU, a licensed piece of software with opinions about hardware, a kernel setting, a steady high-utilization workload whose unit economics reward commitment pricing, or a stateful process that treats the machine as home. The decision it forces is the oldest one in operations: you are now a fleet owner, and fleet ownership is a recurring cost paid in attention, not just in instance-hours.

The trap is inertia. EC2 is the answer teams already know, so it becomes the answer to questions it no longer fits — the two-requests-a-day job sitting on an always-on instance, billed for the 99% of its life it spends waiting. Owning the machine is sometimes the right decision. It should never be the leftover one.

The decision, stated plainly

Ask how much of the machine the work actually needs:

Notice what decides it: not benchmarks, not the age of the service, not what the last team you joined was doing. The three options bill you in three different currencies — Lambda in requests, Fargate in allocated capacity, EC2 in hours and attention — and the right choice is mostly about which currency your workload is rich in. Spiky and rare: pay per request. Steady and container-shaped: pay per allocation. Constant and demanding: pay for the machine and staff it.

The same code will run on all three. That is exactly why the decision matters. When everything works, the service you picked is invisible. It only becomes visible later, in the shape of the bill, the shape of the on-call, and the shape of the migration nobody budgeted for. Choose it while it is still a choice.