RVE LogoReact Video EditorDOCS
RVE SDK/Rendering/Rendering/Lambda/Getting Setup

Getting Setup

Set up self-managed Lambda rendering for the RVE SDK

This guide connects the RVE SDK to a Lambda renderer in your own AWS account. For more detail about each AWS step, see the Remotion Lambda setup guide.

You do these tasks:

  1. Configure AWS permissions.
  2. Deploy the Lambda function, the S3 bucket, and your composition.
  3. Add two API routes to your app.
  4. Connect the editor to the routes.

1. Configure AWS

You do this part one time for each AWS account.

Install the Lambda package

npm i --save-exact @remotion/lambda@4.0.481

Use the same exact version for all @remotion/* packages and remotion. Do not use a ^ range.

Create the role policy

npx remotion lambda policies role
  1. In the AWS console, go to IAM > Policies > Create policy.
  2. Paste the JSON output into the JSON editor.
  3. Name the policy remotion-lambda-policy.

The policy gives the Lambda function the permissions it needs. For example, it lets the function read and write files in S3.

Create the role

  1. Go to IAM > Roles > Create role.
  2. Select Lambda as the use case.
  3. Attach remotion-lambda-policy.
  4. Name the role remotion-lambda-role.

Create a user and access keys

  1. Go to IAM > Users > Create user. Do not give the user console access.
  2. Open the user, then go to Security credentials > Access keys.
  3. Create an access key and save the two values.
  4. Add the values to your .env file:
.env
REMOTION_AWS_ACCESS_KEY_ID=xxx
REMOTION_AWS_SECRET_ACCESS_KEY=xxx

Keep the keys secret

Use these keys only on your server. Do not commit the .env file.

Add the user policy

npx remotion lambda policies user
  1. Open the user, then select Add permissions > Create inline policy.
  2. Paste the JSON output and create the policy.

Validate the permissions

npx remotion lambda policies validate

2. Write and deploy your composition

The Lambda function renders a composition that you write. It must read the size, duration, frame rate, and scale from inputProps, and it must draw items inside LayerBox.

Write a custom composition

Put the entry point at remotion/index.ts in the root of your project.

Deploy with the SDK helper

The SDK includes a deploy() helper. It does these steps:

  1. Deploys the Lambda function, with a CloudWatch log group.
  2. Creates the S3 bucket, or uses the bucket that exists.
  3. Bundles remotion/index.ts from the current working directory and deploys it as a site.
deploy.mjs
import dotenv from 'dotenv';
import { deploy } from '@reactvideoeditor/react-video-editor/utils/remotion/aws-lambda/deploy';

dotenv.config();

await deploy({
  region: 'us-east-1',
  siteName: 'my-video-editor',
  ram: 3009,
  disk: 10240,
  timeout: 240,
});
node deploy.mjs
OptionTypeDefaultDescription
regionAwsRegion'us-east-1'The AWS region for the function, bucket, and site
siteNamestring'my-awesome-site'The site name. A deploy with the same name replaces the site
ramnumber3009The function memory in MB
disknumber10240The function disk size in MB
timeoutnumber240The function timeout in seconds
  • The helper needs @remotion/lambda. It is an optional peer dependency of the SDK.
  • If REMOTION_AWS_ACCESS_KEY_ID and REMOTION_AWS_SECRET_ACCESS_KEY are both missing, the helper shows a message and stops without a deploy.
  • The helper prints the function name, the bucket name, and the site name.

To get the Serve URL of the site and the function name, run these commands:

npx remotion lambda sites ls
npx remotion lambda functions ls

Deploy with the CLI

You can also deploy with the CLI:

npx remotion lambda functions deploy
npx remotion lambda sites create remotion/index.ts --site-name=my-video-editor

The second command prints the Serve URL.

Redeploy after changes

Deploy the site again when you change the composition or upgrade the @remotion/* packages. If you use the same site name, the Serve URL does not change.

Check the AWS limits

npx remotion lambda quotas

A new AWS account can have a low concurrency limit. If you need more concurrent renders, ask AWS to increase the limit.

3. Add the API routes

The HttpRenderer sends a POST request to {endpoint}/render and then to {endpoint}/progress. For the endpoint /api/lambda, create these two App Router files:

  • app/api/lambda/render/route.ts
  • app/api/lambda/progress/route.ts

Add these values to your .env file:

.env
REMOTION_AWS_REGION=us-east-1
REMOTION_FUNCTION_NAME=remotion-render-4-0-481-mem3009mb-disk10240mb-240sec
REMOTION_SERVE_URL=https://remotionlambda-xxxx.s3.us-east-1.amazonaws.com/sites/my-video-editor/index.html

These names are examples. Use the function name and the Serve URL from your deploy.

Render route

The request body is { id, inputProps }. The response must contain the renderId and the bucketName.

app/api/lambda/render/route.ts
import { renderMediaOnLambda, type AwsRegion } from '@remotion/lambda/client';

import { collectFontInfos } from '@/lib/collect-font-infos';

export async function POST(request: Request) {
  const { id, inputProps } = await request.json();

  const { renderId, bucketName } = await renderMediaOnLambda({
    region: process.env.REMOTION_AWS_REGION as AwsRegion,
    functionName: process.env.REMOTION_FUNCTION_NAME!,
    serveUrl: process.env.REMOTION_SERVE_URL!,
    // The id is the editor projectId. It must equal your composition id.
    composition: id,
    inputProps: {
      ...inputProps,
      fontInfos: collectFontInfos(inputProps.tracks),
    },
    codec: 'h264',
    downloadBehavior: { type: 'download', fileName: 'video.mp4' },
  });

  return Response.json({ renderId, bucketName });
}

collectFontInfos is the function from Fonts. Without it, server renders use the fallback font.

Progress route

The request body is { id, bucketName }. The id is the renderId from the render route. The response must use one of these shapes:

| { type: 'progress'; progress: number } // progress from 0 to 1
| { type: 'done'; url: string; size: number } // size in bytes
| { type: 'error'; message: string }
app/api/lambda/progress/route.ts
import { getRenderProgress, type AwsRegion } from '@remotion/lambda/client';

export async function POST(request: Request) {
  const { id, bucketName } = await request.json();

  const progress = await getRenderProgress({
    renderId: id,
    bucketName,
    region: process.env.REMOTION_AWS_REGION as AwsRegion,
    functionName: process.env.REMOTION_FUNCTION_NAME!,
  });

  if (progress.fatalErrorEncountered) {
    return Response.json({
      type: 'error',
      message: progress.errors[0]?.message ?? 'The render failed.',
    });
  }

  if (progress.done && progress.outputFile) {
    return Response.json({
      type: 'done',
      url: progress.outputFile,
      size: progress.outputSizeInBytes ?? 0,
    });
  }

  return Response.json({ type: 'progress', progress: progress.overallProgress });
}

The HttpRenderer also accepts a response in the shape { type: 'success', data }. It reads data from that shape.

4. Connect the editor

Create an HttpRenderer with the base endpoint of your routes. Give it to the customRenderer prop.

app/page.tsx
'use client';

import { useMemo } from 'react';
import { ReactVideoEditor } from '@reactvideoeditor/react-video-editor/react-video-editor';
import { HttpRenderer } from '@reactvideoeditor/react-video-editor/utils/http-renderer';
import '@reactvideoeditor/react-video-editor/styles.css';

export default function Page() {
  const lambdaRenderer = useMemo(
    () =>
      new HttpRenderer(
        '/api/lambda',
        { type: 'lambda', entryPoint: '/api/lambda' },
        { name: 'Lambda' },
      ),
    [],
  );

  return (
    <ReactVideoEditor
      // Must equal the composition id, unless your render route uses a fixed id.
      projectId="my-project"
      fps={30}
      customRenderer={lambdaRenderer}
    />
  );
}
  • The editor uses cloud mode because an HttpRenderer is enabled by default.
  • The props set the render mode. The editor has no control that changes the mode. It does not change to browser mode when a cloud render fails.
  • If you set enableWebRender={true} and pass { enabled: false } as the third argument, the editor uses browser mode.
  • The name option sets the renderer name in the export dialog. The default is 'Cloud'.