This guide is for the engineers, architects, and operators who deploy, configure, extend, and operate Remotion — not just author videos with it. It covers the monorepo architecture, the rendering pipeline internals, the complete configuration reference, server-side and cloud rendering APIs, deployment (AWS Lambda, Google Cloud Run, Docker), security, and the contributor-level structure of the codebase.
For day-to-day authoring (components, hooks, the Studio), see the companion User Guide.
Note
Reference baseline:
remotionand@remotion/*at4.0.472, React 19, packaged as a Bun workspace monorepo. The native renderer is a Rust binary that embeds FFmpeg.
Table of Contents
- Executive Summary
- System Architecture
- The Rendering Pipeline
- Requirements & Environments
- Configuration Mastery
- Server-Side Rendering API
- AWS Lambda Deployment
- Google Cloud Run Deployment
- Containerized & Self-Hosted Rendering
- Security
- Operations
- Repository Structure (Contributor Reference)
- Internals Deep Dive
- Testing Strategy
- Potential Improvements & Tech Debt
- Quick Reference
1. Executive Summary
Remotion is a framework for programmatic video creation using React. It is distributed as the remotion package plus a large family of @remotion/* packages, all developed in a single monorepo and released in lockstep.
What it does, operationally:
- Renders React components to MP4/WebM/ProRes/GIF/stills by driving a headless Chrome browser frame-by-frame and stitching frames into a video with an embedded FFmpeg (compiled into a Rust compositor binary).
- Provides a browser-based Studio for preview/editing and a
@remotion/playerfor embedding live playback in apps. - Offers three rendering surfaces: local CLI, Node SSR APIs, and cloud (AWS Lambda, Google Cloud Run, or your own containers).
Key architectural properties an operator should know:
| Property | Implication |
|---|---|
| Deterministic, frame-indexed rendering | Renders are reproducible and trivially parallelizable (split frames across workers). |
| Self-contained binaries | Chrome (Headless Shell) is auto-downloaded; FFmpeg is embedded in the Rust compositor. No system FFmpeg/Chrome required. |
| Platform-specific native packages | @remotion/compositor-{platform} optional deps deliver the right Rust binary per OS/arch. |
| Lockstep versioning | Every remotion/@remotion/* must match exactly; mismatches are a top support issue. |
| Source-available, two-tier license | Not MIT — larger for-profits need a paid company license (see Security/Licensing). |
2. System Architecture
2.1 High-level component map
┌──────────────────────────────────────────────────────────────────────┐
│ AUTHORING SURFACE │
│ remotion (core) ── React components, hooks, interpolate/spring │
│ @remotion/cli ── studio / render / still / lambda / cloudrun ... │
│ @remotion/studio ── browser Studio UI (+ @remotion/studio-server) │
│ @remotion/player ── embeddable live playback │
└───────────────┬──────────────────────────────────────────────────────┘
│ bundles the project with…
▼
┌──────────────────────────────────────────────────────────────────────┐
│ BUILD SURFACE │
│ @remotion/bundler ── Webpack (or Rspack, experimental) bundle │
│ @remotion/babel-loader ── transforms project code │
└───────────────┬──────────────────────────────────────────────────────┘
│ serves the bundle to…
▼
┌──────────────────────────────────────────────────────────────────────┐
│ RENDER SURFACE │
│ @remotion/renderer ── orchestrates headless Chrome (frame capture) │
│ │ │
│ ├─ Chrome Headless Shell (auto-downloaded) │
│ └─ @remotion/compositor-<platform> (Rust binary, embeds FFmpeg)│
│ → extracts video frames, encodes/stitches output │
└───────────────┬──────────────────────────────────────────────────────┘
│ scale-out variants:
▼
┌──────────────────────────────────────────────────────────────────────┐
│ @remotion/lambda + @remotion/lambda-client + @remotion/serverless │
│ @remotion/cloudrun │
│ → split frames across many serverless workers; output to S3 / GCS │
└──────────────────────────────────────────────────────────────────────┘
2.2 Package layering
The monorepo (remotion-monorepo, Bun workspaces under packages/**, ~250 packages) organizes into layers:
| Layer | Packages | Role |
|---|---|---|
| Core runtime | remotion (packages/core) |
Components, hooks, animation math, the frame model. Browser-safe. |
| Build | @remotion/bundler, @remotion/babel-loader |
Bundle the user project (Webpack; Rspack experimental). |
| Render | @remotion/renderer, @remotion/compositor-* |
Orchestrate Chrome + the Rust/FFmpeg compositor. |
| Studio | @remotion/studio, @remotion/studio-server, @remotion/studio-shared |
The local editing app and its dev server. |
| Player | @remotion/player, @remotion/player-a11y |
Embeddable playback. |
| Cloud | @remotion/lambda, @remotion/lambda-client, @remotion/cloudrun, @remotion/serverless, @remotion/serverless-client |
Distributed rendering on AWS/GCP. |
| CLI | @remotion/cli, create-video |
Command-line entry + scaffolder. |
| Media | @remotion/media-parser, @remotion/webcodecs, @remotion/media, @remotion/media-utils, @remotion/convert |
Media parsing/encoding/analysis. |
| Feature/helper | transitions, shapes, paths, noise, three, skia, lottie, gif, motion-blur, effects, google-fonts, fonts, layout-utils, preload, captions, rive, … |
Optional capabilities. |
| Cross-language clients | lambda-go, lambda-python, lambda-ruby, lambda-php (+ examples) |
Trigger Lambda renders from non-JS backends. |
| Tooling/internal | eslint-config*, eslint-plugin, docusaurus-plugin, docs, it-tests, skills, mcp |
Linting, docs, integration tests, AI tooling. |
2.3 The Rust compositor
The most important non-JS component. @remotion/renderer declares the platform binaries as optional dependencies, so npm installs only the one matching the host:
@remotion/compositor-darwin-arm64 @remotion/compositor-linux-x64-gnu
@remotion/compositor-darwin-x64 @remotion/compositor-linux-x64-musl
@remotion/compositor-linux-arm64-gnu @remotion/compositor-win32-x64-msvc
@remotion/compositor-linux-arm64-musl
The compositor source lives in packages/compositor/rust (entry main.rs, with command modules under rust/commands). It embeds FFmpeg and is responsible for extracting exact video frames (used by <OffthreadVideo>), encoding, and stitching frames into the final container. Communication between the Node renderer and the compositor is over a long-running child process. Memory behavior is informed by the Lambda environment (compositor/rust/memory.rs reads AWS_LAMBDA_FUNCTION_MEMORY_SIZE).
Note
Because the binary is native and platform-specific, an operator's environment must match a supported
(os, libc, arch)triple — notablygnuvsmuslon Linux. Docker base images must provide a compatible libc.
3. The Rendering Pipeline
A render — local or cloud — follows the same conceptual stages:
1. BUNDLE bundle() builds the project with Webpack → a "serve URL"
(a static bundle directory served over HTTP).
2. SELECT selectComposition()/getCompositions() loads the bundle in
Chrome, runs calculateMetadata(), resolves dimensions/
fps/duration/props for the target composition.
3. RENDER renderFrames()/renderMedia() opens Chrome tab(s), advances
the frame, waits for delayRender() handles to clear,
screenshots each frame (JPEG/PNG).
4. EXTRACT For <OffthreadVideo>, the Rust compositor extracts exact
source frames off the main thread.
5. STITCH stitchFramesToVideo() pipes frames + audio to the embedded
FFmpeg with the chosen codec/pixel format/bitrate.
6. OUTPUT Final file written locally, or uploaded (S3/GCS) in cloud.
Concurrency model: renderMedia opens multiple Chrome tabs (concurrency, default ≈ half the CPU threads) and renders frames in parallel, encoding as it goes unless disallowParallelEncoding is set. In the cloud, frames are additionally split across many serverless invocations (framesPerLambda), each producing a chunk that is concatenated.
Determinism guarantees make this safe: each frame is a pure function of its index, so splitting frames across tabs/workers always reconstructs the same video.
4. Requirements & Environments
4.1 Runtime requirements
| Component | Requirement / Notes |
|---|---|
| Node.js / Bun | Node 18+ or Bun. The monorepo itself uses bun@1.3.3. |
| Chrome | Chrome Headless Shell auto-downloaded by Remotion; manage with npx remotion browser ensure. |
| FFmpeg | Embedded in the compositor; do not install separately. |
| libc | On Linux, match gnu or musl to the installed compositor binary. |
| GPU (optional) | Hardware acceleration / specific OpenGL backends available via config. |
4.2 Linux shared libraries
Headless Chrome on minimal Linux needs system libraries (fonts, GTK, NSS, etc.). On Debian/Ubuntu the typical set includes libnss3, libdbus-1-3, libatk1.0-0, libgbm1, libasound2, libxkbcommon0, libpango-1.0-0, and font packages. The official Dockerfiles in packages/dockerfiles encode a known-good set — use them as the source of truth.
4.3 Multi-process on Linux
By default Remotion passes --single-process to Chromium on Linux for stability. Removing it (faster, multi-process) is opt-in via Config.setChromiumMultiProcessOnLinux(true) but may fail on some distros or when window-server libraries are missing.
5. Configuration Mastery
Configuration is supplied two ways, with CLI flags taking precedence over the config file:
remotion.config.ts— calls on theConfigobject (affects the Studio and CLI renders).- CLI flags — per-invocation overrides.
- SSR/Lambda options — explicit parameters to
renderMedia,renderMediaOnLambda, etc.
// remotion.config.ts
import {Config} from '@remotion/cli/config';
Config.setVideoImageFormat('jpeg');
Config.setConcurrency(8);
Config.setCodec('h264');
Config.setCrf(18);
Important
The config format is flat since Remotion 4. The old grouped namespaces (
Config.Bundling.*,Config.Rendering.*,Config.Output.*,Config.Log.*,Config.Preview.*,Config.Puppeteer.*) now throw an error directing you to the flat equivalents. Several setters were also renamed:setQuality → setJpegQuality,setImageFormat → setStillImageFormat/setVideoImageFormat,setLevel → setLogLevel,setTimeoutInMilliseconds → setDelayRenderTimeoutInMilliseconds.
5.1 Output & encoding
| Setter | Default | Purpose |
|---|---|---|
setCodec(c) |
h264 |
h264, h265, vp8, vp9 (or set gif via image format/codec). |
setVideoImageFormat(f) |
jpeg |
jpeg or png (png = transparency, slower). |
setStillImageFormat(f) |
png |
Still output format. |
setJpegQuality(q) |
80 |
0–100. |
setCrf(n) |
codec-dependent | Constant Rate Factor (lower = higher quality). |
setPixelFormat(f) |
yuv420p |
Incl. yuva420p (alpha), 10-bit variants. |
setProResProfile(p) |
hq |
4444-xq,4444,hq,standard,light,proxy. |
setX264Preset(p) |
— | ultrafast…placebo. |
setAudioCodec(c) |
codec-dependent | pcm-16,aac,mp3,opus. |
setAudioBitrate(b) / setVideoBitrate(b) |
— | Target bitrates (video bitrate mutually exclusive with CRF). |
setEncodingMaxRate / setEncodingBufferSize |
— | FFmpeg -maxrate / -bufsize. |
setColorSpace(cs) |
default | Opt into bt709. |
setMuted(b) |
false |
Drop audio. |
setEnforceAudioTrack(b) |
true |
Add silent track if needed. |
setForSeamlessAacConcatenation(b) |
false |
Prepare AAC for seamless concat (Lambda chunking). |
setNumberOfGifLoops(n) |
null (infinite) |
GIF loop count. |
setEveryNthFrame(n) |
1 |
GIF frame sampling. |
setImageSequence(b) / setImageSequencePattern(p) |
false |
Output image sequence; filename pattern with [frame]/[ext]. |
setOutputLocation(s) |
out/{composition}.{codec} |
Output path. |
setOverwriteOutput(b) |
true |
Overwrite existing output. |
setMetadata(obj) |
— | Embed metadata into the file. |
setPreferLosslessAudio(b) |
false |
Prefer lossless audio. |
setSampleRate(n) |
48000 |
Output audio sample rate. |
5.2 Render behavior & quality of frames
| Setter | Default | Purpose |
|---|---|---|
setConcurrency(n|"50%") |
half CPU threads | Parallel render tabs. |
setScale(n) |
1 |
Scale output dimensions. |
setFrameRange([a,b]|n) |
all | Subset of frames (single number = a still). |
setDelayRenderTimeoutInMilliseconds(ms) |
30000 |
Per-frame async timeout. |
overrideHeight/Width/Fps/Duration(n) |
— | Override composition metadata. |
setDisallowParallelEncoding(b) |
false |
More memory-efficient, possibly slower. |
setHardwareAcceleration(opt) |
— | GPU-accelerated encoding option. |
setOffthreadVideoCacheSizeInBytes(n) |
— | Cache for OffthreadVideo frame extraction. |
setBeepOnFinish(b) |
— | Studio beep when render completes. |
5.3 Chromium
| Setter | Default | Purpose |
|---|---|---|
setBrowserExecutable(path) |
auto | Use a specific browser binary. |
setChromeMode(mode) |
— | Chrome Headless Shell vs Chrome for Testing. |
setChromiumHeadlessMode(b) |
true |
Show a real browser to observe rendering. |
setChromiumDisableWebSecurity(b) |
false |
Disable CORS/security (only if you must). |
setChromiumIgnoreCertificateErrors(b) |
false |
Accept self-signed certs. |
setChromiumOpenGlRenderer(r) |
swangle on Lambda, else null |
egl,angle,swiftshader,swangle,vulkan,angle-egl. |
setChromiumUserAgent(s) |
default | Custom UA (render only). |
setChromiumDarkMode(b) |
false |
Force dark mode. |
setChromiumMultiProcessOnLinux(b) |
false |
Remove --single-process on Linux. |
setEnableCrossSiteIsolation(b) |
false |
COOP/COEP headers in the Studio. |
5.4 Bundling & Studio
| Setter | Default | Purpose |
|---|---|---|
setEntryPoint(src) |
— | Default entry so you needn't pass it on the CLI. |
setPublicDir(dir) |
public |
Location of static assets. |
setPublicPath(path) |
— | Public path for the bundle. |
setCachingEnabled(b) |
true |
Webpack bundle cache. |
overrideWebpackConfig(fn) |
identity | Customize the Webpack config. |
setExperimentalRspackEnabled(b) |
false |
Use Rspack instead of Webpack. |
setStudioPort(n) / setRendererPort(n) |
auto | HTTP ports (setPort deprecated). |
setShouldOpenBrowser(b) |
true |
Open a browser when the Studio starts. |
setMaxTimelineTracks(n) |
15 |
Studio timeline track cap. |
setKeyboardShortcutsEnabled(b) |
true |
Studio shortcuts. |
setWebpackPollingInMilliseconds(n) |
null |
Polling for HMR on VMs/remote dirs. |
setNumberOfSharedAudioTags(n) |
0 |
Shared audio tags (Player autoplay). |
setBufferStateDelayInMilliseconds(n) |
— | Delay before buffering UI shows. |
setIPv4(b) |
false |
Bind Studio to IPv4. |
setBundleOutDir(dir) |
build |
Output of npx remotion bundle. |
setAskAIEnabled(b) |
— | Studio "Ask AI" modal. |
setExperimentalClientSideRenderingEnabled(b) |
false |
WIP client-side rendering. |
setAllowHtmlInCanvasEnabled(b) |
false |
HTML-in-canvas capture path. |
5.5 Logging, diagnostics & licensing
| Setter | Default | Purpose |
|---|---|---|
setLogLevel('trace'|'verbose'|'info'|'warn'|'error') |
info |
Verbosity (verbose = browser logs + IO). |
setRepro(b) |
— | Collect a reproduction bundle for bug reports. |
setBinariesDirectory(dir) |
— | Where platform binaries/libs live. |
setDotEnvLocation(file) |
.env |
Custom env file. |
setPublicLicenseKey(key) |
— | Company license key (or 'free-license'). |
setBenchmarkRuns(n) / setBenchmarkConcurrencies(s) |
3 / null |
benchmark command tuning. |
5.6 Lambda-specific config
| Setter | Purpose |
|---|---|
setDeleteAfter(day) |
Auto-expire S3 output after a period. |
setEnableFolderExpiry(b) |
Allow S3 bucket lifecycle expiry. |
setLambdaInsights(b) |
Enable Lambda Insights on deploy. |
6. Server-Side Rendering API
@remotion/bundler + @remotion/renderer expose the rendering pipeline as Node functions for use in CI, queues, and web backends.
6.1 Core functions
| Function | Package | Purpose |
|---|---|---|
bundle(options) |
bundler | Bundle the project; returns a serve URL (bundle directory). |
getCompositions(serveUrl, opts) |
renderer | List all compositions (after running their calculateMetadata). |
selectComposition({serveUrl, id, inputProps}) |
renderer | Resolve a single composition's metadata. |
renderMedia({...}) |
renderer | Render a video/audio to a file. |
renderStill({...}) |
renderer | Render a single image. |
renderFrames({...}) |
renderer | Render frames only (advanced/custom stitching). |
stitchFramesToVideo({...}) |
renderer | Encode frames into a container. |
ensureBrowser(opts?) |
renderer | Pre-download/verify Chrome. |
openBrowser(...) |
renderer | Open a reusable browser instance. |
makeCancelSignal() |
renderer | Create a cancel token to abort a render. |
getVideoMetadata(...) |
renderer | Inspect a media file. |
6.2 bundle() options
const serveUrl = await bundle({
entryPoint: './src/index.ts',
webpackOverride: (config) => config, // optional
publicPath: null, // optional
onProgress: (p) => console.log(`${p}%`),
});
6.3 Full SSR example
import {bundle} from '@remotion/bundler';
import {selectComposition, renderMedia, makeCancelSignal} from '@remotion/renderer';
const serveUrl = await bundle({entryPoint: './src/index.ts'});
const inputProps = {title: 'Quarterly Report'};
const composition = await selectComposition({serveUrl, id: 'Report', inputProps});
const {cancel, cancelSignal} = makeCancelSignal();
await renderMedia({
serveUrl,
composition,
codec: 'h264',
outputLocation: `out/${composition.id}.mp4`,
inputProps,
concurrency: 8,
crf: 18,
cancelSignal,
onProgress: ({progress}) => console.log(`${Math.round(progress * 100)}%`),
});
Tip
In a long-running server, call
bundle()once at startup (or per deploy) and reuse the serve URL across many renders. Bundling is the slowest stage; resolving and rendering per request against a cached bundle is far faster. UseensureBrowser()at boot to avoid a cold download on the first request.
7. AWS Lambda Deployment
@remotion/lambda renders at massive scale by fanning a single render out across many Lambda invocations, each rendering a slice of frames. @remotion/lambda-client is the lighter client used to trigger/poll renders (e.g. from a backend or another language's bindings).
7.1 Setup sequence
1. Configure AWS credentials & IAM (user policy + role policy).
2. deployFunction() → creates the Lambda render function.
3. deploySite() → bundles your project and uploads it to S3 (serve URL).
4. renderMediaOnLambda() → triggers a distributed render.
5. getRenderProgress() → poll until done; output lands in S3.
7.2 Function defaults & limits
From @remotion/lambda-client constants:
| Setting | Min | Default | Max |
|---|---|---|---|
| Memory (MB) | 512 | 2048 | 10240 |
| Timeout (s) | — | 120 | (AWS max 900) |
| Ephemeral storage / disk (MB) | 512 | 2048 | 10240 |
Tune memory/timeout/disk at deployFunction time; the function name encodes its memory/disk/timeout so multiple sized functions can coexist.
7.3 Triggering a render
import {renderMediaOnLambda, getRenderProgress} from '@remotion/lambda/client';
const {renderId, bucketName} = await renderMediaOnLambda({
region: 'us-east-1',
functionName: 'remotion-render-4-0-472-mem2048mb-disk2048mb-120sec',
serveUrl: 'https://remotionlambda-...s3...amazonaws.com/sites/my-site/index.html',
composition: 'MyComp',
codec: 'h264',
inputProps: {title: 'Hello'},
framesPerLambda: 20, // chunk size; affects parallelism
privacy: 'public', // 'public' | 'private' | 'no-acl'
downloadBehavior: {type: 'download', fileName: 'video.mp4'},
});
let done = false;
while (!done) {
const p = await getRenderProgress({renderId, bucketName, functionName, region: 'us-east-1'});
if (p.fatalErrorEncountered) throw new Error(p.errors[0]?.message);
done = p.done;
}
Selected renderMediaOnLambda inputs: region, functionName, serveUrl, composition, codec, inputProps, imageFormat, crf, jpegQuality, pixelFormat, proResProfile, privacy, framesPerLambda, concurrency/concurrencyPerLambda, frameRange, scale, everyNthFrame, maxRetries, outName, chromiumOptions, downloadBehavior, overwrite, webhook, forceWidth/Height/Fps/DurationInFrames, audioCodec, metadata, storageClass, envVariables, and licenseKey. The output includes renderId, bucketName, and several CloudWatch log URLs.
7.4 Other Lambda APIs
renderStillOnLambda, getCompositionsOnLambda, getRenderProgress, getFunctions/deleteFunction, getSites/deleteSite, getOrCreateBucket, presignUrl, speculateFunctionName, and estimatePrice. Webhooks (webhook option) notify your endpoint on completion/error.
Warning
framesPerLambdacontrols the parallelism/cost trade-off: smaller chunks = more invocations (faster, more overhead), larger chunks = fewer invocations (cheaper, slower). For seamless audio across chunks, enablesetForSeamlessAacConcatenation. Validate against Lambda's concurrency quotas — large fan-outs can hit account limits.
7.5 Cross-language clients
@remotion/lambda-go, @remotion/lambda-python, @remotion/lambda-ruby, and @remotion/lambda-php let non-JS backends trigger renders by calling the deployed function directly (using REMOTION_AWS_* credentials and the function/region/serve-URL).
8. Google Cloud Run Deployment
@remotion/cloudrun is the GCP analog (offered by the Remotion team as an alpha). The model mirrors Lambda: deploy a Cloud Run service and a site, then render against them, with output to a Cloud Storage bucket.
8.1 GCP permissions
The installer (packages/cloudrun/src/gcpInstaller) provisions a service account whose role needs Cloud Run + Cloud Storage permissions, including: run.services.create/update/list, run.routes.invoke, run.operations.get, storage.buckets.create/get/list, storage.objects.create/list/delete, and run.services.get/setIamPolicy (to toggle authenticated/unauthenticated invocation). APIs to enable: run.googleapis.com and cloudresourcemanager.googleapis.com.
8.2 CLI
npx remotion cloudrun services deploy
npx remotion cloudrun sites create src/index.ts
npx remotion cloudrun render <serve-url> <composition>
Region is read from REMOTION_GCP_REGION; project/credentials from REMOTION_GCP_PROJECT_ID, REMOTION_GCP_CLIENT_EMAIL, and the private key.
9. Containerized & Self-Hosted Rendering
For predictable, isolated rendering on your own infrastructure, run the SSR APIs inside a container.
- Dockerfiles:
packages/dockerfilesholds reference images with the correct Chrome shared libraries and a compatible libc — start from these rather than hand-rolling. - libc match: use a base image whose libc (
gnu/glibcvsmusl/Alpine) matches the installed@remotion/compositor-linux-*binary. Alpine requires themuslvariant. - Fonts: install font packages in the image or load fonts at runtime via
@remotion/fonts/@remotion/google-fonts, or rendered text may fall back/clip. - Ensure binaries at build time: run
npx remotion browser ensureduring image build so the first render doesn't download Chrome. - Resources: headless Chrome is memory-hungry; size containers per
concurrency. UsesetDisallowParallelEncoding(true)to trade speed for lower peak memory.
A minimal pattern:
# Use a base with glibc; install Chrome deps (see packages/dockerfiles for the full list)
RUN apt-get update && apt-get install -y libnss3 libdbus-1-3 libatk1.0-0 \
libgbm1 libasound2 libxkbcommon0 libpango-1.0-0 libcairo2 fonts-liberation
COPY . /app
WORKDIR /app
RUN npm ci && npx remotion browser ensure
CMD ["node", "render-server.js"]
10. Security
10.1 Threat surface
| Surface | Risk | Mitigation |
|---|---|---|
| Arbitrary input props | Props are serialized into the bundle and rendered; untrusted HTML/strings could enable XSS-like injection in the rendered DOM. | Validate with Zod schema; treat input props as untrusted; sanitize any user-supplied HTML. |
| Remote assets | <Img>/<OffthreadVideo>/fetch pull arbitrary URLs during render. |
Allowlist domains; run renders in a sandboxed network/container. |
setChromiumDisableWebSecurity |
Disables CORS and Chrome security. | Keep false unless strictly necessary; never combine with untrusted content. |
setChromiumIgnoreCertificateErrors |
Accepts invalid TLS. | Avoid in production. |
| Cloud credentials | REMOTION_AWS_* / REMOTION_GCP_* grant render + storage access. |
Use least-privilege IAM (the deploy tooling generates scoped policies); store secrets in a secret manager, not in the bundle. |
| S3/GCS output privacy | privacy: 'public' makes renders world-readable. |
Default to private and serve via presigned URLs (presignUrl). |
| Code execution in the bundle | The project bundle runs in Chrome with full JS. | Only render trusted project code; isolate per-tenant renders. |
10.2 Network isolation
Renders execute untrusted-ish web content in headless Chrome. For multi-tenant or user-submitted compositions, run each render in an isolated container/VM with restricted egress, and avoid mounting host credentials into the render environment.
10.3 IAM (Lambda)
The Lambda tooling distinguishes a user policy (what your deploying principal may do) from a role policy (what the function may do at runtime). Use the generated, scoped policies rather than broad admin credentials; simulatePermissions/validation helpers exist to verify setup.
10.4 Licensing as a compliance concern
Remotion is source-available, not MIT. Larger for-profit organizations must hold a paid company license; eligibility for the free license is limited to individuals, for-profits with ≤3 employees, non-profits, and evaluators. Set Config.setPublicLicenseKey() accordingly and track this as a procurement/compliance item. The terms change slightly in Remotion 5.0; the authoritative text is LICENSE.md.
11. Operations
11.1 Upgrades
Because of lockstep versioning, never bump packages individually:
npx remotion upgrade # upgrades all remotion packages together
npx remotion versions # verify alignment
npx remotion versions reports installed versions and flags mismatches — wire it into CI to fail builds with drift.
11.2 Monitoring renders
- Local/SSR: use
onProgresscallbacks; setsetLogLevel('verbose')to surface browser logs and IO. - Lambda:
getRenderProgressreturns progress, errors, and CloudWatch log URLs (main + per-render + Lambda Insights). Poll it; alert onfatalErrorEncountered. - Benchmarking:
npx remotion benchmarkmeasures render time across concurrency settings to right-size hardware.
11.3 Cost & cleanup (cloud)
- Set
setDeleteAfter/setEnableFolderExpiryto auto-expire S3 output and control storage cost. estimatePriceprojects Lambda render cost from memory/disk/duration.- Periodically
deleteSite/deleteFunctionfor stale deployments.
11.4 Performance levers
| Lever | Effect |
|---|---|
concurrency |
More tabs = faster, more memory. |
framesPerLambda (cloud) |
Smaller = more parallel, more overhead. |
setHardwareAcceleration / --gl |
GPU encoding/rendering where available. |
premountFor on data-heavy sequences |
Avoids per-frame stalls. |
setOffthreadVideoCacheSizeInBytes |
Reduces repeated frame extraction. |
setDisallowParallelEncoding |
Lower peak memory, slower. |
setScale/setJpegQuality/setCrf |
Trade quality for speed/size. |
12. Repository Structure (Contributor Reference)
remotion-monorepo/ (private root; Bun workspaces; packages/**)
├── package.json bun@1.3.3, catalog of pinned deps, ci scripts
├── LICENSE.md two-tier license terms
└── packages/
├── core/ → `remotion` (components, hooks, math)
├── cli/ → `@remotion/cli` (commands, config, flags)
├── bundler/ → Webpack bundling, esbuild-loader, fast-refresh
├── babel-loader/ → project code transforms
├── renderer/ → headless Chrome orchestration, FFmpeg bridge
├── compositor/rust/ → Rust binary (embeds FFmpeg); main.rs + commands/
├── compositor-<platform>/ → prebuilt native binaries (optional deps)
├── studio/ studio-server/ studio-shared/ → the Studio app + dev server
├── player/ player-a11y/ → embeddable Player
├── lambda/ lambda-client/ serverless/ serverless-client/ → AWS rendering
├── cloudrun/ → GCP Cloud Run rendering (+ gcpInstaller)
├── lambda-go/ -python/ -ruby/ -php/ → non-JS clients
├── media-parser/ webcodecs/ media/ media-utils/ convert/ → media stack
├── transitions/ shapes/ paths/ noise/ three/ skia/ lottie/
│ gif/ motion-blur/ effects/ svg-3d-engine/ rive/ → feature packages
├── google-fonts/ fonts/ layout-utils/ preload/ captions/ → assets/text/audio
├── install-whisper-cpp/ openai-whisper/ elevenlabs/ → speech/captions
├── create-video/ → project scaffolder
├── template-*/ → starter templates (helloworld, tiktok, next, …)
├── docs/ → Docusaurus documentation site
├── it-tests/ → cross-package integration tests
├── eslint-config*/ eslint-plugin/ → linting
└── skills/ mcp/ codex-plugin/ → AI/agent integrations
12.1 Build & dev scripts (root)
| Script | Purpose |
|---|---|
bun run build |
Build all packages. |
bun run watch |
Watch-build during development. |
bun run test |
Run the test suite. |
bun run ci |
Full CI pipeline. |
bun run ts-build |
TypeScript build. |
bun run stylecheck / formatting / format |
Lint/format (uses oxfmt/eslint). |
bun run testssr / testlambda / teste2e / testtemplates / testwebcodecs / testwebrenderer |
Targeted test groups. |
Dependencies are centralized via the Bun catalog in the root package.json (e.g. react, zod, @aws-sdk/*, three, mediabunny), so versions are pinned once and referenced as catalog: in each package.
13. Internals Deep Dive
13.1 The frame model (packages/core)
useCurrentFramereads from a timeline context; in the Studio/Player it's driven by playback, during render it's set per captured frame.<Sequence>shifts the frame for its subtree by adjusting the timeline context (from, with cumulative offsets for nesting and negative-offset handling).premountFor/postmountFormount children early/late while frozen and hidden (opacity:0,pointer-events:none) so async work warms up.interpolatevalidates ranges (strictly increasing input, equal lengths), supports per-segment easing arrays,posterize, and string/tuple output ranges (CSSscale/translate/rotateand numeric tuples).springintegrates a damped harmonic oscillator (damping/mass/stiffness), caches advancement, supportsfrom/to/delay/reverse/durationInFrames, and usesmeasureSpringfor natural duration.delayRender/continueRendermaintain a set of handles onwindow; rendering proceeds only when all handles clear. During render, each handle arms a timeout (timeoutInMilliseconds, default 30s, minus a 2s safety margin) and supportsretries.staticFilerejects remote/relative/absolute/public/-prefixed paths and URL-encodes safely.
13.2 The render orchestrator (packages/renderer, 143 files)
Coordinates: opening/pooling Chrome, navigating to the served bundle, advancing frames, screenshotting, and bridging to the Rust compositor for video-frame extraction and FFmpeg encoding. Notable areas: compositor/ (binary bridge), browser/ (Chrome control), assets/ (asset collection), options/ (the browser-safe option definitions surfaced as CLI flags and config setters via BrowserSafeApis), and serve-handler/.
Note
The single source of truth for render options is
BrowserSafeApis.optionsin the renderer's client. The CLI flags, theConfig.*setters, and the SSR/Lambda option objects are all generated from these definitions — which is why a flag, a config setter, and an API field for the same option always behave identically.
13.3 Media stack
@remotion/media-parser(parseMedia) is a pure-JS parser for many containers (ISO-BMFF/MP4, WebM/Matroska, RIFF/WAV, transport streams, MP3, AAC, FLAC, M3U/HLS) used to read metadata and frames without FFmpeg.@remotion/webcodecs(convertMedia, encoders/decoders) does in-browser conversion via the WebCodecs API.<OffthreadVideo>relies on the Rust compositor for exact frame extraction during render;<Video>(HTML5) is preview-oriented.
13.4 Studio (packages/studio, studio-server)
A React app served by @remotion/studio-server's preview server (routes under preview-server/routes, hot-middleware for HMR, codemods for editing user code from the UI). It talks to the same bundler/renderer used by the CLI, so what you preview matches what you render.
14. Testing Strategy
| Layer | Where | Notes |
|---|---|---|
| Unit tests | src/test inside each package |
Co-located with source; run via bun test. |
| Integration tests | packages/it-tests |
Cross-package flows: rendering, SSR, templates, Lambda/Cloud Run, codemods, monorepo wiring. |
| Visual regression | packages/web-renderer/src/test/__screenshots__, packages/effects/src/visual-test |
Screenshot baselines for rendering fidelity. |
| Targeted suites | root scripts | testssr, testlambda, teste2e, testtemplates, testwebcodecs, testwebrenderer, testlayoututils, testeffectsvisual. |
| Skill evals | packages/skills-evals |
Evaluate AI agent skills against Remotion tasks. |
When contributing: build (bun run build), run the relevant targeted suite, run stylecheck/formatting, and verify version alignment. New options should be added to the renderer's option definitions so they propagate to CLI/config/SSR consistently.
15. Potential Improvements & Tech Debt
These are observations from the codebase and its issue tracker, useful for maintainers and downstream operators.
- V5 license change.
LICENSE.mdnotes the terms change in Remotion 5.0 — track this for compliance and updatesetPublicLicenseKeyworkflows accordingly. - Deprecated-but-live APIs.
startFrom/endAt(→trimBefore/trimAfter), groupedConfig.*namespaces,setQuality/setImageFormat/setLevel/setTimeoutInMilliseconds, andsetPortremain as deprecated shims. A future major could remove them; downstream code should migrate. - Native-binary surface. The platform-matrix of
@remotion/compositor-*binaries (gnu/musl × arch × OS) is a recurring source of environment mismatches. Operators should pin and verify the correct variant in CI/Docker. - Experimental features. Rspack bundling, client-side rendering, HTML-in-canvas, and the new
@remotion/media(experimental_Audio/experimental_Video) are behind flags — treat as unstable in production. - Open issues of operational interest (from the tracker at the documented commit): a frame-cache bug with non-monotonic video PTS (#8167) — relevant when rendering sources with irregular timestamps; the remainder are Studio UX items.
- Cloud Run alpha. GCP support is explicitly alpha; AWS Lambda is the more battle-tested distributed path.
16. Quick Reference
Deployment commands
# Local / SSR
npx remotion render [entry] [comp] [out]
npx remotion still [comp] [out] --frame=N
npx remotion bundle # produce a deployable bundle
# AWS Lambda
npx remotion lambda functions deploy
npx remotion lambda sites create src/index.ts
npx remotion lambda render <serve-url> <comp>
npx remotion lambda functions ls / rm
# Google Cloud Run
npx remotion cloudrun services deploy
npx remotion cloudrun sites create src/index.ts
npx remotion cloudrun render <serve-url> <comp>
# Maintenance
npx remotion upgrade
npx remotion versions
npx remotion browser ensure
npx remotion benchmark
npx remotion gpu
Lambda function defaults
| Setting | Min | Default | Max |
|---|---|---|---|
| Memory (MB) | 512 | 2048 | 10240 |
| Timeout (s) | — | 120 | 900 (AWS) |
| Disk/ephemeral (MB) | 512 | 2048 | 10240 |
Operational environment variables
| Variable | Use |
|---|---|
REMOTION_AWS_ACCESS_KEY_ID / REMOTION_AWS_SECRET_ACCESS_KEY |
Lambda credentials (preferred over generic AWS vars). |
AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY |
Standard AWS credentials (fallback). |
REMOTION_GCP_PROJECT_ID / REMOTION_GCP_CLIENT_EMAIL / REMOTION_GCP_REGION |
Cloud Run config. |
REMOTION_VERSION |
Reported by the studio server / used in routing. |
REMOTION_PARTITIONED_RENDERING |
Renderer parallel-encoding control. |
AWS_LAMBDA_FUNCTION_MEMORY_SIZE |
Read by the compositor to tune memory behavior. |
__RESERVED_IS_INSIDE_REMOTION_LAMBDA |
Internal marker for the Lambda routine (do not set). |
Render option ↔ surface mapping
| Concept | CLI flag | Config setter | SSR/Lambda field |
|---|---|---|---|
| Codec | --codec |
setCodec |
codec |
| Concurrency | --concurrency |
setConcurrency |
concurrency |
| CRF | --crf |
setCrf |
crf |
| Scale | --scale |
setScale |
scale |
| Frame range | --frames |
setFrameRange |
frameRange |
| Image format | --image-format |
setVideoImageFormat |
imageFormat |
| Timeout | --timeout |
setDelayRenderTimeoutInMilliseconds |
timeoutInMilliseconds |
| Pixel format | --pixel-format |
setPixelFormat |
pixelFormat |
| Muted | --muted |
setMuted |
muted |
All three surfaces are generated from the same option definitions, so behavior is consistent across them.