Compute¶
Every compute choice on AWS is really the same question asked at a different altitude: how much of the stack do you want to own, and how much are you willing to pay someone else to own instead? EC2, EKS, Fargate, and Lambda are four answers to that one question, not four unrelated services — and picking wrong shows up later as either wasted ops effort or a workload that can't do what you need it to.
Scope: the AWS module page introduced the EKS control-plane/data-plane split, the EC2 purchasing decision, ASG scaling policies, and Karpenter vs Cluster Autoscaler. This page zooms out to the full compute spectrum — where EC2, EKS, Fargate, and Lambda each sit on the manage-everything ↔ manage-nothing axis — and goes one layer deeper on each stop.
TL;DR — in 30 seconds:
- Every AWS compute option trades control for less ops work along one spectrum: EC2 (you own everything) → EKS-on-nodes (AWS owns the control plane) → Fargate (AWS owns the node) → Lambda (AWS owns everything but your function code).
- Moving right isn't "better" — it's giving up DaemonSets, privileged Pods, and always-on guarantees in exchange for not operating that layer at all.
- Blend EC2 purchasing options rather than picking one: Reserved/Savings Plans for the known baseline, On-Demand for the unpredictable peak, Spot for anything interruption-tolerant.
1. The compute spectrum¶
Every AWS compute option trades control for less operational work. Moving right, you give up a layer of the stack and AWS takes it over in exchange.
flowchart LR
EC2["EC2<br/>(you manage the VM,<br/>the OS, and everything on it)"]
EKS["EKS on nodes<br/>(you manage the node;<br/>AWS manages the control plane)"]
FAR["Fargate<br/>(you manage the container;<br/>AWS manages the node)"]
LAM["Lambda<br/>(you manage the function code;<br/>AWS manages everything else)"]
EC2 -->|"hand off patching,<br/>capacity, node lifecycle"| EKS
EKS -->|"hand off the node itself"| FAR
FAR -->|"hand off the container<br/>and the always-on process"| LAM
There is no "best" stop on this spectrum — only the right trade for a given workload. A team running a steady, predictable fleet of long-lived services usually sits on the left (EC2/EKS-on-nodes); a team running bursty, event-driven, short-lived work usually sits on the right (Fargate/Lambda).
2. EC2 + Auto Scaling — the foundation¶
EC2 is a virtual machine you fully control: OS, patching, and everything running on it. Two decisions shape its cost and elasticity.
The purchasing decision — how you pay for the instance:
| Option | Commitment | Best for | Risk |
|---|---|---|---|
| On-Demand | none | spiky or unpredictable load | most expensive per hour |
| Reserved / Savings Plans | 1–3 years | steady, known baseline load | pay even if unused |
| Spot | none, but AWS can reclaim it | fault-tolerant, interruptible work (batch, CI runners) | can be reclaimed with ~2 minutes' notice |
Most production fleets blend these: Reserved/Savings Plans cover the known baseline, On-Demand absorbs the unpredictable peak, and Spot soaks up anything that can tolerate an interruption.
The scaling decision — an Auto Scaling Group (ASG) launches and terminates instances from a launch template, driven by a scaling policy:
- Target tracking — "keep average CPU at 50%" — the default reach-for; you name a target, ASG does the math.
- Step scaling — add/remove a specific instance count per alarm threshold crossed — used when the response needs to be more aggressive than target tracking's smooth curve.
- Scheduled scaling — pre-provision ahead of a known traffic pattern (a 9am login spike, a Black Friday sale) instead of reacting after the alarm fires.
3. EKS — one layer under the control-plane/data-plane split¶
The module page already drew the line: AWS runs the control plane, you own the data plane. Inside "you own the data plane," there are three concrete shapes a node can take:
| Node type | Who patches the OS | Who scales it | Notes |
|---|---|---|---|
| Managed node group | AWS-provided AMI, you trigger the update | an ASG AWS manages for you | the common default |
| Self-managed ASG | you | you (your own ASG + launch template) | full control, full ownership — custom AMIs, unusual instance types |
| Fargate profile | AWS (there is no node) | AWS | see §4 — no DaemonSets, no privileged Pods |
Whichever shape you pick, every worker node authenticates to the control plane with an IAM role (not a static credential), and the node's IAM role is a separate concern from IRSA (a Pod's own identity) — a node's role grants it what it needs to be a node (join the cluster, pull images, write logs); IRSA grants an individual Pod what it needs (read one S3 bucket), scoped narrower than anything the node itself can do.
Karpenter vs Cluster Autoscaler, one layer deeper than "same job, different philosophy":
- Cluster Autoscaler watches one or more existing, fixed-shape node groups and resizes them — it adds/removes nodes of the shape you already defined.
- Karpenter skips the node group entirely: it watches for unschedulable Pods and provisions a node shaped for those specific Pods directly from EC2, then bin-packs future Pods onto the best-fit instance type it already has running. The trade is flexibility (right-sized nodes, faster provisioning) for a bit more setup complexity than "just resize this ASG."
4. Fargate — removing the node¶
Fargate runs containers (EKS Pods or ECS tasks) without giving you a node to manage at all — no AMI, no patching, no ASG. You define the Pod/task's CPU and memory; AWS provisions the exact-sized compute underneath it and bills per second for that Pod/task's actual usage.
The trade-offs are real, not just marketing footnotes:
- No DaemonSets — there's no shared node to run one Pod-per-node on.
- No privileged Pods — Fargate's isolation model doesn't allow host-level access.
- Slower cold start — provisioning a right-sized sandbox per Pod takes longer than scheduling onto an already-running node. (Go deeper: the official EKS on Fargate considerations — optional, not required to defend this page.)
Reach for Fargate when you want zero node-lifecycle work and your workload doesn't need a DaemonSet, privileged access, or the fastest possible startup — logging/monitoring agents that must run node-wide are the most common reason a cluster keeps some managed nodes even after adopting Fargate for the rest.
5. Lambda — removing the container too¶
Lambda goes one step further than Fargate: you don't even manage a container image's lifecycle as a running process. You upload function code (or a container image used only as a packaging format), AWS invokes it per event, and you're billed per invocation and per millisecond of execution — nothing runs, and nothing is billed, between invocations.
- A cold start is the latency of provisioning a fresh execution environment for the first invocation (or the first after a scale-up) — negligible for background/event work, a real concern for a latency-sensitive synchronous request path.
- Concurrency limits cap how many invocations run at once per account/function; a sudden burst beyond that limit throttles rather than queuing indefinitely, which shapes how you design the event source in front of it — the module page's decoupling primitives (SQS in particular) exist partly to smooth a Lambda's invocation rate against its concurrency limit.
- Lambda pairs naturally with API Gateway (HTTP trigger) and Step Functions (orchestrating many Lambdas into a workflow) — both named on the module page's serverless layer.
6. Choosing — a workload-shaped decision, not a maturity ladder¶
flowchart TD
Q1{"Long-running, or<br/>short/event-driven with<br/>no always-on process?"}
Q1 -->|"short/event-driven"| Lambda["Lambda"]
Q1 -->|"long-running"| Q2{"Containerized?"}
Q2 -->|"no — full OS<br/>control needed"| EC2["EC2 (+ ASG)"]
Q2 -->|"yes"| Q3{"Want zero<br/>node-lifecycle ownership?"}
Q3 -->|"yes"| Fargate["Fargate"]
Q3 -->|"no — team already<br/>owns Kubernetes"| EKS["EKS on nodes"]
| Workload shape | Reach for |
|---|---|
| Steady, long-running service; full OS control needed | EC2 (+ ASG) |
| Long-running containerized service, team already owns Kubernetes | EKS on nodes |
| Containerized service/job, want zero node-lifecycle ownership | Fargate |
| Short, event-driven, bursty; no need for an always-on process | Lambda |
Moving right on the spectrum isn't strictly "better" — it's giving up the DaemonSet/privileged-Pod/ always-on guarantees the left side offers, in exchange for not operating that layer at all. Most real platforms run more than one stop on the spectrum simultaneously, matched per workload.
7. How this connects¶
- This extends the AWS module page's "Compute — EKS" section (control-plane/data-plane split, EC2 purchasing, ASG policies, Karpenter vs Cluster Autoscaler) with the rest of the compute spectrum — Fargate and Lambda — and the concrete node-type table underneath "data plane."
- The Prove gate's ownership-split question ("how does that split change between a managed node group and Fargate?") draws directly on §3's node-type table and §4's Fargate trade-offs.
Common gotchas
- Treating the compute spectrum as a maturity ladder ("Lambda is the advanced option"). Fix: it's a workload-shape trade, not a maturity signal — a steady, long-running service belongs on EC2/ EKS-on-nodes regardless of how "modern" Lambda looks.
- Putting a DaemonSet-dependent workload (a node-wide logging/monitoring agent) on Fargate. Fix: Fargate has no shared node to run one Pod-per-node on — DaemonSets need actual nodes, often the reason a cluster keeps some managed nodes even after adopting Fargate elsewhere.
- Confusing a node's IAM role with a Pod's IRSA identity. Fix: the node's role grants what it needs to be a node (join the cluster, pull images); IRSA scopes an individual Pod to only what it needs — treating them as the same permission boundary over-grants every Pod on that node.
- Assuming Karpenter and Cluster Autoscaler do the same job at the same layer. Fix: Cluster Autoscaler resizes a node group you already defined; Karpenter provisions a right-sized node directly from EC2 for the pending Pods' actual shape — picking the wrong one means either fighting a fixed node-group shape or taking on Karpenter's setup complexity for no reason.
- Ignoring Lambda concurrency limits when designing the event source. Fix: a burst beyond the limit throttles rather than queuing indefinitely — put a buffer (like SQS) in front of a Lambda whose invocation rate can spike past what it's provisioned to handle concurrently.
Check yourself — a workload is a long-running containerized service, and your team already runs Kubernetes. Where does it belong on the compute spectrum, and why not Fargate?
EKS on nodes — Fargate is the right call when you want zero node-lifecycle ownership, but a steady, long-running service with an existing Kubernetes team doesn't need to give that control up; Fargate's real cost (no DaemonSets, no privileged Pods, slower cold start) buys nothing extra here.
Check yourself — why can't Fargate run a DaemonSet?
A DaemonSet needs a real, addressable node to guarantee one Pod per node. Fargate removes the node entirely — you define a Pod's CPU/memory and AWS provisions an exact-sized sandbox underneath it — so there's no shared node for a DaemonSet's per-node guarantee to attach to.
You can defend this when you can place EC2, EKS-on-nodes, Fargate, and Lambda on the same manage-everything ↔ manage-nothing spectrum, explain what each node type in EKS still makes you responsible for, state why Fargate can't run a DaemonSet or a privileged Pod, and pick correctly between all four for a workload described in one sentence.