---
title: Remotion Lambda or Cloudflare Containers? We built both, timed both, and chose Lambda | Plainlamp
description: We render our lesson videos with Remotion. We built a fully private route on Cloudflare Containers, timed it against Remotion Lambda on the same 14-minute lesson, and Lambda won on speed and cost. How both work, the numbers, and how we made Lambda's public step small.
url: https://plainlamp.com/blog/remotion-lambda-or-cloudflare
---

29 October 2026, 11 min read

# Remotion Lambda or Cloudflare Containers? We built both, timed both, and chose Lambda

Plainlamp turns a hard PDF, such as a paper, a policy or a standard, into a short narrated video lesson and a guide. We draw the videos with [Remotion](https://www.remotion.dev/), which makes video from React code: a headless Chrome draws each frame, and ffmpeg turns the frames into an MP4.

On an 8-core desktop, drawing a 14-minute lesson takes about 38 minutes. We wanted it in the cloud, split over many machines. We built two ways to do that: Remotion Lambda on AWS, and our own setup on Cloudflare Containers. Then we rendered the same lesson on both, on the same day. This post has the numbers, how each route works, and what we learned.

## The result

![Bars for one 14-minute lesson of 24,411 frames, from the start to the downloaded video: Lambda with 199 pieces 2 min 15 s, Lambda with Remotion's own 150 pieces 2 min 55 s, Cloudflare with 49 chunks 6 min 32 s, Cloudflare with 16 chunks 11 min 37 s. Cost per video: Lambda US$0.27 (compute only), Cloudflare 49 chunks at most US$1.03, 16 chunks at most US$0.65. All in, about US$0.30 on Lambda and at most US$1.05 to 1.10 on Cloudflare.](https://plainlamp.com/blog/remotion-lambda-or-cloudflare/render-1-compare-1080.webp)

One lesson, 24,411 frames, timed from the start of the render job to the downloaded video:

| Route | Time | Cost per video |
| --- | --- | --- |
| Lambda, 199 pieces | 2 min 14 s and 2 min 16 s | US$0.26 to 0.28 (compute only) |
| Lambda, 150 pieces (Remotion's own choice) | 2 min 55 s | US$0.27 (compute only) |
| Cloudflare, 49 chunks | 6 min 31 s and 6 min 34 s | at most US$1.00 to 1.06 |
| Cloudflare, 16 chunks | 11 min 37 s | at most US$0.65 |

The Lambda costs are Remotion's own estimate for the function time. With data transfer, storage requests and logs added, a video costs about US$0.30 on Lambda. The Cloudflare costs count every second of each container's vCPU, memory and disk, about US$0.00011 a second for the largest size. That is an upper bound, because Cloudflare bills vCPU only while it is in use. All in, a video costs at most about US$1.05 to 1.10 on Cloudflare. Neither figure takes off the free amounts that come with each plan.

So Lambda was about 3 times faster than our fastest Cloudflare setup, and cost up to 3.5 times less. Lambda is now our default.

Why does the faster Cloudflare setup cost more than the slower one? It splits the video into 49 chunks instead of 16. With backups and the join, that is 54 or 55 containers instead of 17, and each container pays for its own start-up of about 80 seconds.

## The one catch with Lambda: a public site

Remotion Lambda renders from a "site": your bundled video code, uploaded to an S3 bucket. The render functions open that site by its web address, so the site must be readable by anyone who has the address. Our drawing code is general, but a lesson is made from someone's document, and nothing of a document should sit at a public address.

That is why we built the Cloudflare route first. When the numbers came in, we looked for a way to keep Lambda's speed and make its public step small. This is what we do now:

![Flow: a new site for each render holds drawing code, pictures and fonts, and a scan refuses lesson text; it sits at a random address with 128 random bits, in a bucket that can't be listed; 199 Lambdas each draw 123 frames from it; the site is deleted as soon as the frames are drawn, after 96 to 134 seconds; the lesson's script, plan, voices and video go to Lambda privately.](https://plainlamp.com/blog/remotion-lambda-or-cloudflare/render-3-site-1080.webp)

- **A new site for every render.** It holds only the drawing code, the pictures and the fonts. A scan refuses the site if it holds 8 or more words in a row of any lesson's text. Today it holds no wording from any document.
- **At an address nobody can guess.** The site's folder name carries 128 random bits. The bucket lets anyone read files under the sites folder only, and lets nobody list them. Before uploading anything, the render checks that a listing request without credentials is refused.
- **Deleted as soon as the frames are drawn.** In our benchmark the site was up for 96 and 99 seconds with 199 pieces, and 134 seconds with 150. If a render is killed, the next render deletes any site older than 2 hours.
- **Everything about the lesson stays private.** Its words and plan go to Lambda as input props, which Remotion keeps as private files. The voice recordings are private files with signed links. The finished video is private, downloaded, then deleted.

For a lesson whose files must stay fully private, we keep the Cloudflare route.

## What can render video on Cloudflare?

- **Workers: no.** Remotion says rendering on edge functions is not possible: there is no file system, and the size and time limits are too small.
- **Browser Run (Browser Rendering): no.** It drives Cloudflare's own browser, Remotion needs to start its own Chrome, and there is no ffmpeg.
- **Containers: yes.** A container is a normal Linux container image, built from a Dockerfile, that a Worker starts on demand. Containers have been generally available since April 2026. The largest standard size has 4 vCPU, 12 GiB of memory and 20 GB of disk.

We were not the first. Remotion has a [single-container demo](https://github.com/remotion-dev/cloudflare-containers-demo), and Remotion paid for a [distributed example](https://github.com/odysseaspapadimas/cloudflare-remotion-example) by Odysseas Papadimas that splits a video over many containers. We read both closely, borrowed ideas from the second, and wrote our own version, mainly to keep the containers off the internet.

## How our Cloudflare route works

![Flow: a Worker that answers only with a secret token; a job object (Durable Object) that splits the video, starts every chunk and keeps the cap; 49 containers with 4 vCPU and 12 GiB each and no internet; a backup container for a slow chunk, the first one done wins; one more container joins the chunks with combineChunks; private EU storage (R2), deleted after.](https://plainlamp.com/blog/remotion-lambda-or-cloudflare/render-4-private-1080.webp)

**1. The image.** A Debian Node 24 image with Chrome Headless Shell, the libraries Chrome needs, and our Remotion bundle baked in. The bundle holds the drawing code, the pictures and the fonts, never a lesson.

- GitHub Actions builds it, so the build doesn't depend on any one machine.
- The base image and the Dockerfile frontend are pinned to digests, and every GitHub action to a commit. The Debian packages and the Chrome download are not pinned yet.
- After each deploy, the deployed bundle is scanned against the text of every lesson. No real lesson is sent until that scan passes.

**2. The Worker.** A small Worker is the only way in. It answers no request without a secret token.

**3. The job object.** Each render is a job, kept in a Durable Object.

- It splits the video into chunks of about 500 frames (at most 60 chunks) and starts them all at once, one container per chunk.
- It keeps every step's state in storage and does its work from alarms, a little at a time. If the object restarts, the next alarm reads the state and carries on.
- It retries a failed chunk on a fresh container, and keeps the cost cap.

**4. Containers with no internet.** The containers start with internet access turned off and hold no token and no storage key. Each container can reach one internal name, which Cloudflare routes to a handler in our Worker. That handler answers only for the files of the container's own job: its inputs, and the chunk files for the container that joins them. The container checks each file's size and SHA-256 before it uses it. When a chunk is done, the job object copies it out.

**5. A backup for a slow chunk.** When a chunk has run longer than the finished ones took (their median), and its progress says a fresh container would finish sooner, the job starts a second container on the same chunk. The first one done wins, and the other is destroyed. Both are paid for, and the cap counts both.

**6. Lambda's chunk settings, copied exactly.** A split video is only seamless if every chunk is rendered the same way. We copied Remotion Lambda's own chunk settings from its source code (Remotion 4.0.524):

- video as h264-ts;
- audio as AAC with forSeamlessAacConcatenation;
- the same framesPerChunk and compositionStart in every chunk;
- audio written to its own file.

When every chunk is in, one more container joins them with Remotion's combineChunks, which is a public function now.

**7. Private storage, then delete everything.** Inputs, chunks and the final MP4 sit in a private R2 bucket with no public address. The containers, the job object and the bucket are all set to the EU jurisdiction. When the video is collected, the job is deleted: every file and every container. A 24-hour alarm in the job object deletes it too, in case that never happens, and the bucket's own rule deletes any job file after 2 days.

## Is a split video the same video?

Each chunk is compressed on its own, so a split video is not byte for byte the same as one rendered in one go. We measured how close it is on an earlier 8-chunk split of a 6-minute test video, on the Cloudflare route, which copies Lambda's chunk settings:

- **Sound:** at the 7 joins, it was the same as the one-container video, sample for sample.
- **Pictures:** a similarity score (SSIM) of 0.9991 against the one-container video, where 1 means identical. The difference is too small to see.

## Things we learned the hard way

![One dot per piece for four runs: Lambda's 199 pieces took 28 to 87 seconds in both runs (each function's time in its log); Cloudflare's 49 chunks were done 81 to 310 seconds after the job started in one run and 107 to 306 seconds in the other, about 80 seconds of container start-up included.](https://plainlamp.com/blog/remotion-lambda-or-cloudflare/render-2-pieces-1080.webp)

- **More Lambdas cost the same and finish sooner.** For this lesson Remotion chose 150 pieces of 163 frames: 2 min 55 s. We asked for 199 pieces of 123 frames, close to Remotion's limit of 200: 2 min 15 s, for the same price. You pay for function time, and the total work barely changes.
- **A Lambda that runs out of time cancels the whole render.** Our first Lambda render used functions with a 120-second limit. It reached that limit and was cancelled, and the time already used was still billed (about US$0.27 for no video). Each piece must fit inside the limit, and so must the whole render, because Remotion's main function stays alive until the end. The functions now have 300 seconds, and our code refuses to start a render whose pieces would not fit.
- **The slowest piece decides, on both routes.** In the functions' own logs, Lambda's pieces took 28 to 87 seconds. On Cloudflare, chunks were done 81 to 310 seconds after the job started, about 80 seconds of that being container start-up. One slow chunk holds the whole video. Smaller chunks, backup containers and containers that fetch their own files made Cloudflare nearly twice as fast as our first split into 16 chunks, but not as fast as Lambda.
- **Budget alerts don't stop anything.** Cloudflare's budget alerts are only emails: they don't pause or cap usage. So the cap is in our code, in layers: the estimate is checked before the job starts, the job object adds what is spent to what the rest will cost before every new container, and max_instances limits how many containers can run at once. On Lambda, every render also needs a cap, and our AWS account has a spend limit.
- **Use destroy(), not stop().** On Cloudflare, stop() only sends SIGTERM. If your process ignores it, the container keeps running and you keep paying. destroy() ends it at once. We destroy each container as soon as its chunk is copied out.
- **Make the bundle reproducible.** Two builds of the same code, a minute apart, gave different bundles: Remotion writes each static file's modification time into index.html. We now give every file one fixed time, so the bundle comes out the same every time. On Cloudflare we build the image once and deploy that exact image by its digest. On Lambda we check the bundle's hash again right before the site goes up.
- **Fonts.** Lambda's Chrome has no Arial, and Arial's licence does not allow it on a server. We now use Liberation Sans Bold everywhere. It is metric-compatible with Arial Bold (the same letter widths), so text takes the same space.
- **On a new AWS account, check your Lambda limit.** A Remotion render can ask for up to 200 functions at once, and a new account's limit can be far lower. AWS raised ours to 1,000 when we asked.

## Which one to choose

**Remotion Lambda, pros:**

- In our test, about 3 times faster, and up to 3.5 times cheaper per video.
- Ready-made: Remotion runs the split, the join and the retries for you.
- Each piece is short, so a slow machine costs seconds, not minutes.

**Remotion Lambda, cons:**

- The site must be readable at its address. Keep it to drawing code, and make the address random and short-lived.
- A function has a hard time limit (at most 15 minutes on AWS). Every piece and the whole render must fit inside it, or the render is cancelled.
- Check your account's concurrency limit first.

**Cloudflare Containers, pros:**

- Nothing can be read without our secret token, and the containers have no internet.
- A container has no fixed time limit.
- The containers, the job object and the storage can all be kept in the EU.
- You control every step: the split, the retries, the backups, the cap and the clean-up.

**Cloudflare Containers, cons:**

- In our test, slower and more expensive: minutes instead of seconds, up to about a dollar a video instead of 30 cents.
- No ready-made package. Remotion offers a template you adopt and then own, and you write and test the job logic yourself.
- No hard spend stop, so you build the cap yourself.
- Building the image needs Docker somewhere. For us that is GitHub Actions.

**Our advice:**

- **Choose Lambda** if your video code can sit at a short-lived address. Make that address random, and delete the site after each render.
- **Choose Cloudflare Containers** if nothing of what you render may ever sit at an open address, even for a minute.

We use both: Lambda by default, and Cloudflare for any lesson that needs the fully private route.

A note on licences: Remotion is free for individuals, non-profits, and for-profit companies with up to three employees. Larger companies need a company licence. See [remotion.dev/license](https://www.remotion.dev/license).

**About Plainlamp.** Plainlamp turns a difficult PDF into a short narrated video and a plain-English guide. Every point shows the page it comes from.

[See what a lesson looks like](https://plainlamp.com/#lesson)

[All posts](https://plainlamp.com/blog)
