EC2 vs Lambda vs Containers on AWS: How to Choose the Right Compute Service
AWS gives you six main compute options: EC2, Lambda, ECS Fargate, EKS, App Runner, and AWS Batch. The broad rule: use Lambda for short, event-driven workloads that can scale to zero. Use containers for long-running stateless services. Use EC2 when you need full OS control, stateful or legacy workloads, or special hardware such as GPUs.
Inside those three buckets, the choice gets more specific. App Runner is the simplest container starting point. ECS Fargate is the middle ground for production containerised services. EKS is the right call only when you genuinely need Kubernetes. AWS Batch handles compute jobs that Lambda cannot: those running longer than 15 minutes or requiring variable hardware.
This guide walks through each option, when to use it, a workload-based decision matrix, real-world examples, and cost guidance. If you only want a tighter three-way comparison, read Lambda vs ECS vs EC2 instead.
How to choose in 60 seconds
- Choose Lambda if your workload is event-driven, completes in under 15 minutes, has bursty or unpredictable traffic, and you want scale-to-zero with no server management.
- Choose App Runner if you have a simple web app or API, already have a Docker image or source repository, and want the lowest-overhead managed container deployment.
- Choose ECS Fargate if you need long-running containerised services with production-grade control (custom networking, task-level IAM, service discovery, blue/green deployments) without managing Kubernetes.
- Choose EKS if your team has Kubernetes expertise, needs multi-cloud portability, or requires cluster-level features like service mesh or custom admission controllers.
- Choose EC2 if you need full OS access, GPU instances, persistent long-lived connections, or legacy software that cannot run in a container.
- Choose AWS Batch if you have discrete compute jobs (data processing, ML training, media encoding) that run longer than 15 minutes and need automatic provisioning without a persistent server.
If you are deploying your first web app or API on AWS with no existing infrastructure constraints, start with App Runner. It requires the least setup and you can migrate to ECS or EC2 once you have a concrete reason to do so.
The plain-English version
Analogy
Lambda is a contractor: shows up when called, bills only for time worked, and leaves when done. EC2 is a full-time hire: on the clock all day whether there is work or not. Containers (App Runner, ECS) are a day-rate worker on a fixed shift: always present and ready, with AWS handling the building. EKS is running a multi-site franchise: enormous power, but you need experienced management to keep it running. Batch is a specialist crew you bring in for big one-off jobs, then send home.
The core trade-off is control versus operational overhead. More control means more things you manage. Serverless and managed container services reduce operational work but impose constraints on runtime duration, state, and available hardware.
How the compute options differ
Virtual machines (EC2)
EC2 provides a full virtualised server. You choose the OS, install software, configure networking, and control every layer down to kernel modules. The instance runs continuously until you stop it. You pay per hour of uptime, whether it is busy or idle. See the EC2 overview for instance types, storage, and pricing models.
Serverless functions (Lambda)
Lambda runs a single function in response to an event. AWS provisions compute, runs your code, and tears down the execution environment when it finishes. Billing is per request and per millisecond of execution. Nothing runs between invocations. The function must complete within 15 minutes. Read more about event-driven compute with Lambda and how Lambda handles scaling automatically.
Managed containers (App Runner, ECS Fargate)
You package your application as a container image and AWS runs it. The container is always running, and AWS manages the underlying infrastructure. ECS Fargate gives you task-level control. App Runner is even more abstracted: load balancers, scaling, and TLS are handled for you automatically. For a broader look at the difference between containers and virtual machines, see containers vs virtual machines in AWS.
Kubernetes containers (EKS)
EKS runs a managed Kubernetes control plane. You define workloads as Kubernetes manifests and EKS schedules them on nodes. Kubernetes adds cluster-level features (horizontal pod autoscaling, service mesh, custom operators) that ECS does not provide. The cost is significantly more operational complexity. Read what Kubernetes is before deciding if you need it.
Batch compute (AWS Batch)
AWS Batch is purpose-built for discrete compute jobs. It manages EC2 or Fargate capacity, queues jobs, and schedules them as resources become available. Jobs run in containers. Unlike Lambda, there is no time limit. Unlike a persistent EC2 instance, there is no idle cost between jobs.
The options at a glance
| Service | Model | You manage | AWS manages |
|---|---|---|---|
| EC2 | Virtual machines | OS, runtime, app, scaling | Physical hardware, hypervisor |
| App Runner | Fully managed containers | Container image or source code | Everything except the app |
| ECS Fargate | Managed containers | Container image, task definition, app | OS, container runtime, cluster |
| EKS | Kubernetes containers | K8s manifests, app, node groups | Control plane, OS (on Fargate nodes) |
| Lambda | Serverless functions | Function code | Everything except the code |
| AWS Batch | Managed batch jobs | Container image, job logic | EC2/Fargate provisioning, job scheduling |
Decision matrix: workload type to service
| Workload | Recommended service | Reason |
|---|---|---|
| Webhook handler | Lambda | Short-lived, event-driven, low frequency |
| REST API, low or unpredictable traffic | Lambda or App Runner | Scale to zero; Lambda for event-driven, App Runner for always-on simplicity |
| REST API, high sustained traffic | ECS Fargate or EC2 | Always-on; Lambda per-request pricing becomes expensive at scale |
| Scheduled job under 15 minutes | Lambda (EventBridge trigger) | Zero idle cost; scales to zero between runs |
| Scheduled job over 15 minutes | AWS Batch or ECS task | Lambda timeout exceeded; Batch handles arbitrary job duration |
| Queue consumer | Lambda (SQS trigger) or ECS | Lambda for short processing tasks; ECS for long-running consumers |
| WebSocket app | EC2 or ECS Fargate | Persistent long-lived connections; Lambda cannot hold connections open |
| ML training | EC2 (GPU) or AWS Batch | Requires GPU hardware and long runtime; Batch handles provisioning automatically |
| ML inference (real-time) | EC2 (GPU) or ECS | Persistent server for consistent low latency; Lambda cold starts add jitter |
| Legacy monolith | EC2 | Assumes long-running process, local filesystem, specific networking requirements |
| Microservices platform | ECS Fargate or EKS | ECS for simplicity; EKS for Kubernetes-native patterns and tooling |
| Batch data processing | AWS Batch | Automatic provisioning, job queuing, no persistent server required |
When EC2 is the right choice
EC2 gives you a full virtual machine. Use it when:
- Full OS control is required. Custom kernel modules, privileged processes, or hardware features that are unavailable in containers or Lambda.
- Persistent long-lived connections. WebSocket servers, game servers, and real-time communication apps keep connections open for minutes or hours. Lambda cannot hold a connection open between invocations.
- Special hardware. GPU instances (p4d, g5, p5) for ML training and inference. Instances with large local NVMe storage. High-memory instances for in-memory databases. Lambda and Fargate do not expose GPUs.
- Legacy software. Applications that assume a long-running process, depend on local filesystem state across requests, or have networking requirements that are hard to replicate in containers.
- Sustained high-throughput workloads. EC2 Reserved Instances or Savings Plans can be 30-60% cheaper than on-demand pricing, and at high sustained concurrency they beat Lambda per-request billing. The cross-over depends on your specific workload.
- Stateful workloads. Anything that stores session state in local memory or writes to a local database across requests fits a persistent server better than a stateless container or function.
You manage OS patching, security group configuration, and scaling via Auto Scaling Groups. Operational overhead is higher than any other option on this page.
When Lambda is the right choice
Lambda is best when:
- The workload is event-driven. File processing on S3 upload, HTTP requests via API Gateway, messages from SQS, scheduled timers via EventBridge. Lambda is purpose-built for all of these.
- Traffic is bursty or unpredictable. Lambda scales from zero to thousands of concurrent executions automatically. You pay nothing when no requests arrive.
- You want zero operational overhead. No instances to patch, no capacity to reserve, no health checks to configure beyond IAM and your function code.
- Tasks are short-lived and stateless. Each invocation is independent. Lambda cannot share memory between invocations or maintain open connections between requests.
- Cold starts are acceptable. Background jobs, async processors, and scheduled tasks rarely care about a 200ms-1s startup delay. Latency-sensitive synchronous APIs may need provisioned concurrency or a different service entirely.
Lambda has hard limits worth knowing: 15-minute maximum timeout, 10 GB memory, and a default of 1,000 concurrent executions per account per region (raiseable via a support request). If your workload regularly hits these, containers or EC2 are the better fit. See Lambda vs EC2 for a direct comparison.
If your API needs to respond in under 100ms at p99, a standard Lambda function will occasionally miss that target during cold starts. Provisioned concurrency solves it but adds a fixed hourly cost that erodes Lambda’s pricing advantage. For consistently low-latency requirements, ECS Fargate or App Runner are often simpler and cheaper.
When containers are the right choice
Containers sit between Lambda and EC2: more control than serverless, less operational burden than virtual machines. But “containers” on AWS is not one service. The right container option depends on how much control and complexity you actually need.
App Runner: the simplest starting point
App Runner is the right first container choice for simple web apps and APIs. You provide a container image from Amazon ECR or a source code repository, and App Runner handles the rest: load balancing, auto-scaling, TLS certificates, and health checks. There is no cluster to configure, no task definition to write, and no ALB to set up. App Runner scales to zero when idle, which keeps costs low for low-traffic services. Use it when you want a container deployment as simple as a managed PaaS.
ECS Fargate: production container services without Kubernetes
ECS Fargate is the right choice for long-running production services where you need more control than App Runner provides (custom networking, VPC placement, task-level IAM, service discovery, or blue/green deployments) but do not want to manage Kubernetes. Fargate mode removes the need to manage EC2 node groups: you define CPU and memory per task, and AWS provisions the underlying compute. ECS integrates cleanly with Application Load Balancers, CloudWatch, ECR, and Secrets Manager. It handles the majority of container workloads without the overhead of Kubernetes. See Lambda vs ECS if you are deciding between the two.
EKS: when you actually need Kubernetes
EKS is the right choice when your team genuinely needs Kubernetes-native tooling: Helm charts, custom controllers, service mesh (Istio or Linkerd), GitOps workflows, or multi-cluster federation. EKS also matters when portability across cloud providers is a hard requirement, since Kubernetes manifests are portable in a way that ECS task definitions are not. Before choosing EKS, read what Kubernetes is and honestly assess whether ECS Fargate already covers your needs. See EKS vs ECS for a direct comparison.
EKS is a clear upgrade in power and a clear downgrade in simplicity. Setting up the control plane, node groups, networking plugins, and RBAC correctly can take weeks. A small team running five services does not need this. ECS Fargate handles the same workloads with a fraction of the operational complexity. Migrate to EKS when you have a concrete, specific reason, not because it sounds more production-ready.
Where AWS Batch fits
AWS Batch is purpose-built for batch compute workloads: jobs that run to completion, require significant compute resources, and do not need a server running continuously between runs. Think ML training runs, large-scale data processing, media transcoding, and scientific simulations.
The key differences from Lambda and EC2:
- No time limit. Unlike Lambda’s 15-minute cap, Batch jobs can run for hours.
- Automatic provisioning. Batch manages the EC2 or Fargate fleet. You do not provision servers in advance or pay for them when no jobs are running.
- Job queuing and scheduling. Jobs are submitted to a queue and Batch schedules them as compute becomes available, with support for priorities and job dependencies.
- Variable compute per job. Individual jobs can request different CPU and memory amounts. Batch selects the right instance type for each.
If you are already using Lambda for jobs that keep hitting the 15-minute timeout, migrating to Batch is usually straightforward. Batch uses the same container image format. You keep the same application code and just change what runs it.
Use Batch when you find yourself reaching for Lambda but bumping into the timeout, or reaching for EC2 and spending time building your own job scheduler. Batch handles provisioning, queuing, and teardown so you do not have to.
Real-world use cases
Image upload processing
A user uploads a profile photo. An S3 event triggers a Lambda function that resizes the image and saves three variants back to S3. Lambda is a natural fit: event-driven, short-lived, scales automatically with upload volume, no idle cost.
Customer-facing REST API
A product API serving several million requests per day at p99 latency under 50ms. App Runner or ECS Fargate behind an Application Load Balancer: always running, consistent latency, no cold start risk. Lambda would add cold start variability and cost more at this sustained volume.
Long-running background worker
A video transcoding job triggered when a user uploads a file. The job runs 45 minutes. Lambda cannot handle this. AWS Batch accepts the job, provisions a suitable EC2 instance, runs the transcoding container, and terminates the instance when done.
Microservices platform
Fifteen microservices with different scaling requirements and inter-service HTTP calls. ECS Fargate for most teams; EKS if the team needs Kubernetes-native tooling or has existing K8s expertise and multi-cloud requirements.
GPU model training
A weekly ML training run on a large dataset that needs a GPU instance and NVMe storage. AWS Batch with EC2 mode provisions the right GPU instance (p4d, g5), runs the training container, and releases the instance when training completes. No idle GPU costs between training runs.
Scheduled report generation
A daily report that queries a database and emails results to customers. Runs in 3 minutes at 2 AM. Lambda with an EventBridge scheduled trigger: zero idle cost, scales to zero between runs, no infrastructure to maintain.
Common mistakes
Using Lambda for everything regardless of fit. Lambda is the right default for event-driven, short-lived workloads, but teams sometimes apply it to APIs with strict p99 latency requirements, jobs that run longer than 15 minutes, or services with sustained high concurrency. Check the hard limits and the cost model before committing.
Defaulting to EC2 out of familiarity. Teams coming from traditional infrastructure often reach for EC2 even when Lambda or App Runner would be simpler, cheaper, and lower-maintenance. EC2 is the right choice for specific reasons (full OS control, GPU hardware, persistent connections, legacy workloads), not as a general default.
Choosing EKS too early. EKS is powerful but introduces significant operational complexity. A team running five services does not need Kubernetes. App Runner or ECS Fargate handles most container workloads below a certain scale. Migrate to EKS when you have a concrete reason, not because it sounds more scalable.
Ignoring cold starts for latency-sensitive workloads. Lambda cold starts range from 100ms to several seconds depending on runtime and function size. Synchronous APIs with p99 latency targets below 100ms need provisioned concurrency (which adds cost) or should use ECS or App Runner where containers are always warm.
Ignoring AWS Batch for actual batch workloads. Teams often try Lambda and hit the 15-minute timeout, then fall back to a persistent EC2 instance and build their own job scheduler. AWS Batch solves this directly (job queuing, automatic provisioning, job dependencies) without extra service cost beyond the underlying compute.
EC2 vs Lambda vs containers: which is cheaper?
There is no universal answer. The right cost model depends on traffic pattern, idle time, and operational overhead.
Lambda wins when
- Traffic is low or unpredictable. You pay nothing when nothing runs. Always-on options pay for idle time that Lambda avoids entirely.
- Workloads are short and bursty. Lambda’s per-request billing rewards low utilisation. Short functions with sporadic traffic are often near-free.
- You factor in operational cost. No patching, no ASG configuration, no instance management. Fewer engineers needed to keep the system running.
EC2 and containers win when
- Traffic is sustained and high. A flat hourly rate for always-on compute beats per-request billing once concurrency stays consistently high throughout the day.
- Reserved Instances or Savings Plans apply. EC2 reserved pricing is 30-60% lower than on-demand, making it significantly cheaper than Lambda at scale.
- Workloads are long-running. Lambda’s per-millisecond billing makes jobs that run for minutes expensive quickly. A container running the same job for an hour is often far cheaper.
Rough rule of thumb
Lambda tends to be cheaper for low-volume APIs and bursty event processing. ECS Fargate or EC2 tends to be cheaper for services that are busy most of the day. The exact cross-over depends on function duration, memory, and whether provisioned concurrency is needed. Model your specific workload before committing.
AWS pricing changes over time and varies by region. Use the AWS cost estimation guide and the AWS Pricing Calculator to model your workload accurately. See also EC2 cost optimisation and Lambda cost optimisation for service-specific guidance.
Summary
- Lambda: event-driven, short-lived, stateless workloads with bursty traffic. No idle cost. Hard 15-minute timeout.
- App Runner: simplest managed container service. Best for straightforward web apps and APIs with no Kubernetes needed.
- ECS Fargate: long-running containerised services. More control than App Runner, far less complexity than Kubernetes.
- EKS: Kubernetes-native tooling, multi-cloud portability, cluster-level features. Choose it when you genuinely need it, not by default.
- EC2: maximum flexibility. Full OS control, GPU hardware, persistent connections, legacy software. Highest operational overhead.
- AWS Batch: discrete compute jobs with no time limit. Automatic provisioning and scheduling. Ideal when Lambda’s 15-minute limit is the barrier.
- Cost: Lambda wins at low or spiky traffic. EC2 and containers win at high sustained throughput. Model your workload to find the actual cross-over.
Frequently asked questions
Should I use Lambda or ECS for an API?
Lambda works well for APIs with bursty or unpredictable traffic, where scale-to-zero matters and some latency variability is acceptable. ECS Fargate is better for APIs with sustained, predictable traffic where consistent low latency is required and you want always-on containers. For a simple web app or API with moderate traffic, App Runner is the simplest option of the three.
When is EC2 cheaper than Lambda?
EC2 becomes cheaper than Lambda once your workload reaches sustained, high-concurrency traffic. The cross-over depends on request volume, duration, and memory, but as a rough rule: if your function would run at high concurrency for most of the day, reserved EC2 or ECS Fargate will usually cost less. Lambda wins at low or spiky traffic where idle time would otherwise be wasted.
Is App Runner better than ECS for simple web apps?
For simple web apps and APIs, App Runner is the simpler choice. It removes the need to configure clusters, task definitions, load balancers, and service auto-scaling separately. You point it at a container image or source repository and it handles the rest. ECS Fargate gives you more control and flexibility, but that control comes with significantly more configuration work.
Do I need Kubernetes or is ECS enough?
Most teams do not need Kubernetes. ECS Fargate handles long-running containerised services with far less complexity. EKS is worth considering when you have deep Kubernetes expertise on the team, need multi-cloud portability, or require cluster-level features like custom admission controllers, service mesh, or advanced workload scheduling that ECS does not support natively.
Where does AWS Batch fit compared with Lambda and EC2?
AWS Batch is designed for discrete compute jobs that run to completion: data processing, ML training, media transcoding. Unlike Lambda, Batch has no 15-minute timeout. Unlike EC2, it provisions and de-provisions compute automatically. Use Batch when you have jobs that need more than 15 minutes, require variable compute resources, and do not need a persistent server running between jobs.