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:

Duration and cost across memory sizesTry it yourself
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

  • Duration per function versus configured memory; Max Memory Used in 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.