R RenderComp
remotion aws-lambda render-cost video-rendering typescript

Remotion Lambda Cost Formula: framesPerLambda and GB-Seconds

By RenderComp Team Editorial policy

Every Remotion render is deterministic: the same composition, the same frame index, the same output. Lambda exploits that property by splitting a video into independent frame ranges and rendering each range in a separate invocation. Because frames share no mutable state, chunk 0 to 19 and chunk 20 to 39 run concurrently without coordination.

That parallelism is what makes Remotion Lambda fast. It is also why the cost is non-obvious. You are not paying for one function running for the duration of your video. You are paying for many functions running simultaneously, each billing AWS at its own rate for its own wall-clock seconds.

The number you most directly control is framesPerLambda. Setting it affects concurrency, per-invocation duration, and total GB-seconds in ways that compound quickly at scale.


The Chunk Model

When you call renderMediaOnLambda(), Remotion computes the renderer invocation count from totalFrames and framesPerLambda:

const chunkCount = Math.ceil(totalFrames / framesPerLambda);

A 30-second video at 30 fps produces 900 frames. With framesPerLambda set to 20, that splits into 45 renderer invocations. One additional invocation runs the stitcher that concatenates encoded segments with FFmpeg, making 46 total Lambda calls for that render.

Each renderer encodes its assigned frames and writes the output segment to S3. The stitcher reads those segments in order and produces the final output file. All invocations share the same deployed Lambda function and therefore the same memorySizeInMb setting you configured at deploy time.

Remotion’s default framesPerLambda, when you omit it, is calculated dynamically based on video duration. For a 30-second clip it often lands near 20; for a 5-minute clip it scales upward to keep chunk count manageable. The default favors maximum parallelism on the assumption that you have a broad concurrency budget. If your account concurrency limit is constrained, or if you share the region with other high-concurrency workloads, override it explicitly.


The Billing Formula

AWS Lambda bills in 1 ms increments at $0.0000166667 per GB-second. The per-invocation charge is $0.0000002, equivalent to $0.20 per 1 million requests.

The cost for a single invocation:

invocation_cost = (memorySizeInMb / 1024) × (durationMs / 1000) × 0.0000166667
               + 0.0000002

For the full render, sum across all invocations plus the stitcher:

render_cost ≈ Σ invocation_cost(i)  for i in [0, chunkCount]
            + s3_costs

S3 charges cover PUT requests for each chunk segment and GET requests from the stitcher reading them back. At current S3 pricing those are typically a few cents per thousand renders and rarely dominate the total.

At 3,009 MB (approximately 2.94 GB), each second of Lambda time costs 2.94 × 0.0000166667 = $0.000049. If each of the 45 renderer invocations runs for 5 seconds, that is 45 × 5 × $0.000049 = $0.011 for renderers. The stitcher at 8 seconds adds $0.000392. Total Lambda cost for that render: approximately $0.011, plus the per-invocation charge of 46 × $0.0000002 = $0.0000092.

Those 5-second and 8-second figures are illustrative. Actual duration per chunk depends on composition complexity. A composition running heavy spring() animations on every frame costs more per chunk than one doing static layout.


framesPerLambda

framesPerLambda trades cost structure against wall-clock time. Reducing it creates more chunks with shorter duration per invocation. Increasing it produces fewer invocations, each running longer.

import { renderMediaOnLambda } from '@remotion/lambda/client';

const { renderId, bucketName } = await renderMediaOnLambda({
  region: 'us-east-1',
  functionName: 'remotion-render-4-0-0-mem3009mb-disk10240mb-300sec',
  serveUrl: 'https://remotionlambda-yoursite.s3.us-east-1.amazonaws.com/sites/your-id/index.html',
  composition: 'MyVideo',
  inputProps: { title: 'Hello' },
  codec: 'h264',
  framesPerLambda: 40,   // override the default
  memorySizeInMb: 3009,
  timeoutInSeconds: 300,
});

With framesPerLambda: 40 on a 900-frame video, the chunk count drops to Math.ceil(900 / 40) = 23. Each invocation runs roughly twice as long as at 20. The per-request fees halve; the GB-second total stays similar if rendering time scales linearly with frame count, which holds reasonably well for uniform compositions.

Two constraints bound how low framesPerLambda can go. AWS accounts have a default concurrent execution limit of 1,000 across all Lambda functions in a region. A 10-minute, 30 fps video is 18,000 frames. At framesPerLambda: 20, that requires 900 simultaneous invocations before accounting for other functions running in the same account. The second constraint is the Lambda timeout ceiling of 900 seconds per invocation. A complex composition at a high framesPerLambda value can exceed that ceiling when each chunk contains too many expensive frames.

A practical starting range for most standard-length videos is 40 to 80. For a 60-second clip at 30 fps (1,800 frames), framesPerLambda: 60 produces 30 chunks, well inside concurrency limits and far from the per-invocation timeout.


Memory and Render Duration

Lambda allocates CPU proportionally to memory. At 1,769 MB, a function receives one full vCPU. At 3,009 MB, it receives approximately 1.7 vCPUs. More memory costs more per second, but a faster encode finishes in fewer seconds.

The relationship is not linear. Video encoding and JavaScript evaluation include sequential phases that additional CPU cannot compress. Doubling memory often reduces duration by less than half, so the GB-second product can increase at higher memory settings even though wall-clock time decreases.

For CPU-bound compositions with complex spring() calls, many interpolate() steps, or deeply nested Sequence trees, 3,009 MB or 4,096 MB typically shortens wall-clock time enough to be worth the higher per-second rate. Compositions that render mostly static CSS layout without video inputs are less CPU-hungry; 2,048 MB often produces better GB-second efficiency there.

One other sizing decision: diskSizeInMb. The function name suffix encodes this value alongside memory. The stitcher writes the assembled video to /tmp before uploading it to S3, so for videos longer than a few minutes the default 512 MB is often insufficient. Remotion’s recommended 10,240 MB handles most production cases. Disk size does not appear in the GB-second billing formula; it only determines whether the invocation can complete.

The only reliable way to find the memory crossover point for a specific composition is to render it at two settings and compare actual reported costs.


estimatePrice and Actual Cost Tracking

Remotion exposes estimatePrice() in @remotion/lambda/client for cost forecasting before a render starts. It takes the parameters you know upfront and returns a cost object.

import { estimatePrice } from '@remotion/lambda/client';

const totalFrames = 900;  // 30 s × 30 fps
const framesPerLambda = 40;
const chunkCount = Math.ceil(totalFrames / framesPerLambda); // 23
const lambdasInvoked = chunkCount + 1; // add 1 for the stitcher

const estimate = estimatePrice({
  region: 'us-east-1',
  memorySizeInMb: 3009,
  diskSizeInMb: 10240,
  lambdasInvoked,
  durationInMilliseconds: 6000, // expected wall-clock time per invocation
});

console.log(estimate.estimatedDisplayCost); // e.g. "$0.007"
console.log(estimate.estimatedCost);        // number in USD

The durationInMilliseconds field is the expected wall-clock time per invocation, not the clip duration. Calibrate it from previous renders of similar compositions. estimatePrice applies the GB-second formula uniformly to all invocations, treating renderers and the stitcher identically. If your stitcher consistently runs longer than your average renderer chunk, use a weighted average duration to compensate.

After a render completes, getRenderProgress() reports actual accumulated costs:

import { getRenderProgress } from '@remotion/lambda/client';

const progress = await getRenderProgress({
  renderId,
  bucketName,
  functionName: 'remotion-render-4-0-0-mem3009mb-disk10240mb-300sec',
  region: 'us-east-1',
});

if (progress.done) {
  console.log(progress.costs.estimatedCost);        // number in USD
  console.log(progress.costs.estimatedDisplayCost); // e.g. "$0.009"
  console.log(progress.costs.currency);             // "USD"
}

The costs field reflects Lambda invocation duration data collected during the render. It covers Lambda charges only; S3 and data-transfer fees appear separately in AWS Cost Explorer.


Cold Starts and Their Cost

Each renderer invocation may cold-start if its execution environment is not already warm. Remotion Lambda functions bundle a Chromium binary, which pushes cold-start time to 1 to 3 seconds for the 3,009 MB size class. That startup duration bills at the full memory rate.

At framesPerLambda: 20 with 45 chunks, a 2-second cold start on each invocation costs 45 × 2 × (3009 / 1024) × 0.0000166667 = approximately $0.004 in cold-start overhead. At framesPerLambda: 90 with 10 chunks, that overhead falls to 10 × 2 × 2.94 × 0.0000166667 = approximately $0.001.

For renders that fire infrequently, raising framesPerLambda meaningfully reduces that overhead. Lambda Provisioned Concurrency eliminates cold starts but adds a flat hourly charge regardless of render volume. At typical Remotion burst patterns, provisioned concurrency rarely pays for itself below several hundred renders per day.

Pay particular attention to stitcher duration on long videos. FFmpeg concatenation time scales with output duration, not with chunk count, so the stitcher often dominates total cost on clips longer than three minutes even when individual renderer chunks complete quickly.


Wrapping Up

Remotion Lambda render cost reduces to three inputs: Math.ceil(totalFrames / framesPerLambda) for chunk count, memorySizeInMb / 1024 for the per-second GB rate, and average invocation duration. Per-request fees and S3 charges are real but secondary.

estimatePrice converts those three inputs into a USD figure before you invoke a render. progress.costs.estimatedCost gives the measured result afterward. Rendering the same composition at two framesPerLambda values and comparing the returned costs tells you whether the video is chunk-count sensitive or dominated by per-invocation duration. Templates in the RenderComp catalog ship with framesPerLambda: 20 by default; for infrequent renders, testing at 60 or 80 reveals whether the cold-start savings outweigh the marginal increase in wall-clock time.

The next concrete step: render your production composition at framesPerLambda values of 20, 40, and 80, log the three estimatedCost figures at completion, and plot where the curve flattens.

Now available

Get 1,000+ Remotion Templates

Pay once — no subscription. Lifetime updates. TypeScript-first.

View pricing →