Node.js Serverless on AWS Lambda · Data, Concurrency and Cost
Memory, CPU and Cost Tuning
Why the cheapest memory setting is often the most expensive, and how to find the right size for a function.
Scenario
Example app: an image-processing function runs with the minimum memory, 128 MB, "to save money". It runs a few million times a month.
What you see in production
The function takes 4 to 6 seconds per image, times out on large images, and the Lambda bill is higher than the team expected. Users complain about slow uploads.
The naive code
ImageResize:
Type: AWS::Serverless::Function
Properties:
MemorySize: 128 # chosen by default, never measured
Timeout: 10
Reproduce it
Lambda allocates CPU in proportion to memory, so CPU-bound work gets faster as memory grows, until it stops scaling. Cost is memory multiplied by duration:
const pricePerGbSecond = 0.0000166667; // x86 price at the time of writing; check the current pricing page
const invocations = 3_000_000;
// Measured durations (ms) for one image at each memory size, CPU-bound until about 1,769 MB (one full vCPU).
const measurements = [[128, 5200], [512, 1300], [1024, 650], [1769, 380], [3008, 360]];
for (const [mb, ms] of measurements) {
const gbSeconds = (mb / 1024) * (ms / 1000) * invocations;
console.log(`${String(mb).padStart(5)} MB ${String(ms).padStart(5)} ms $${(gbSeconds * pricePerGbSecond).toFixed(2).padStart(7)}/month`);
}
Output
128 MB 5200 ms $ 32.50/month 512 MB 1300 ms $ 32.50/month 1024 MB 650 ms $ 32.50/month 1769 MB 380 ms $ 32.82/month 3008 MB 360 ms $ 52.88/month
Root cause
Memory is the only performance dial on Lambda: CPU, and network bandwidth, scale with it. Below the point where the work stops being CPU-bound, doubling memory roughly halves duration, so cost stays similar while latency drops. Very low memory makes CPU-heavy functions slow, risks timeouts, and is rarely the cheapest option.
The fix
Measure instead of guessing. Run AWS Lambda Power Tuning (an open-source Step Functions state machine) against representative payloads; it invokes the function at several memory sizes and plots cost against speed. Choose based on your goal (lowest cost, lowest latency, or a balance), and re-run after significant code changes. Also:
- Use the arm64 (Graviton) architecture where your dependencies support it; it has a lower price per GB-second and often equal or better performance.
- Reduce work per invocation: batch small items, avoid repeated initialisation, stream instead of buffering.
- Use Compute Optimizer recommendations for functions in production.
Trade-offs
- Lowest cost and lowest latency usually sit at different memory sizes; user-facing paths often justify slightly higher cost.
- More memory also raises the maximum concurrency cost exposure of runaway invocations.
- Graviton needs native dependencies built for arm64; test before switching.
Detect it in production
Durationper function versus configured memory;Max Memory Usedin REPORT logs (memory headroom).- Cost allocation by function (tags) to find the expensive ones.
- Timeouts that correlate with input size.
Key points
- Memory controls CPU; low memory is often slower and not cheaper.
- Measure with Lambda Power Tuning on real payloads.
- Consider arm64; reduce work per invocation.
Further reading: Configuring Lambda function memory · AWS Lambda Power Tuning · AWS Lambda pricing
Practice questions
Why can increasing a function's memory reduce its cost?
Answer
Lambda allocates CPU in proportion to memory. For CPU-bound work, more memory shortens duration roughly proportionally, so the GB-seconds billed can stay the same or drop while latency improves.
How would you choose the right memory size for a function?
Answer
Benchmark it with representative inputs at several memory sizes, for example with AWS Lambda Power Tuning, and pick the setting that matches the goal (cost, speed or balance); re-check after significant changes and use Compute Optimizer for running functions.
What should you check before moving a Node.js function to arm64?
Answer
That all native dependencies (for example image or crypto libraries) have arm64 builds and that tests and performance benchmarks pass on arm64; pure JavaScript code usually works unchanged.
Open in the app to mark lessons complete, take notes and track your progress.