# Remotion Admin & Architecture Guide

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: `remotion` and `@remotion/*` at **`4.0.472`**, React **19**, packaged as a **Bun** workspace monorepo. The native renderer is a **Rust** binary that embeds FFmpeg.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [System Architecture](#2-system-architecture)
3. [The Rendering Pipeline](#3-the-rendering-pipeline)
4. [Requirements & Environments](#4-requirements--environments)
5. [Configuration Mastery](#5-configuration-mastery)
6. [Server-Side Rendering API](#6-server-side-rendering-api)
7. [AWS Lambda Deployment](#7-aws-lambda-deployment)
8. [Google Cloud Run Deployment](#8-google-cloud-run-deployment)
9. [Containerized & Self-Hosted Rendering](#9-containerized--self-hosted-rendering)
10. [Security](#10-security)
11. [Operations](#11-operations)
12. [Repository Structure (Contributor Reference)](#12-repository-structure-contributor-reference)
13. [Internals Deep Dive](#13-internals-deep-dive)
14. [Testing Strategy](#14-testing-strategy)
15. [Potential Improvements & Tech Debt](#15-potential-improvements--tech-debt)
16. [Quick Reference](#16-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/player` for 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

```text
┌──────────────────────────────────────────────────────────────────────┐
│                         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:

```text
@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 — notably `gnu` vs `musl` on Linux. Docker base images must provide a compatible libc.

---

## 3. The Rendering Pipeline

A render — local or cloud — follows the same conceptual stages:

```text
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**:

1. **`remotion.config.ts`** — calls on the `Config` object (affects the Studio and CLI renders).
2. **CLI flags** — per-invocation overrides.
3. **SSR/Lambda options** — explicit parameters to `renderMedia`, `renderMediaOnLambda`, etc.

```ts
// 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

```ts
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

```ts
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. Use `ensureBrowser()` 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

```text
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

```ts
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]
> `framesPerLambda` controls the parallelism/cost trade-off: smaller chunks = more invocations (faster, more overhead), larger chunks = fewer invocations (cheaper, slower). For seamless audio across chunks, enable `setForSeamlessAacConcatenation`. 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

```bash
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/dockerfiles` holds 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`/`glibc` vs `musl`/Alpine) matches the installed `@remotion/compositor-linux-*` binary. Alpine requires the `musl` variant.
- **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 ensure` during image build so the first render doesn't download Chrome.
- **Resources:** headless Chrome is memory-hungry; size containers per `concurrency`. Use `setDisallowParallelEncoding(true)` to trade speed for lower peak memory.

A minimal pattern:

```dockerfile
# 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:

```bash
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 `onProgress` callbacks; set `setLogLevel('verbose')` to surface browser logs and IO.
- **Lambda:** `getRenderProgress` returns progress, errors, and CloudWatch log URLs (main + per-render + Lambda Insights). Poll it; alert on `fatalErrorEncountered`.
- **Benchmarking:** `npx remotion benchmark` measures render time across concurrency settings to right-size hardware.

### 11.3 Cost & cleanup (cloud)

- Set `setDeleteAfter` / `setEnableFolderExpiry` to auto-expire S3 output and control storage cost.
- `estimatePrice` projects Lambda render cost from memory/disk/duration.
- Periodically `deleteSite`/`deleteFunction` for 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)

```text
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`)

- `useCurrentFrame` reads 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`/`postmountFor` mount children early/late while frozen and hidden (`opacity:0`, `pointer-events:none`) so async work warms up.
- `interpolate` validates ranges (strictly increasing input, equal lengths), supports per-segment easing arrays, `posterize`, and string/tuple output ranges (CSS `scale`/`translate`/`rotate` and numeric tuples).
- `spring` integrates a damped harmonic oscillator (`damping`/`mass`/`stiffness`), caches advancement, supports `from`/`to`/`delay`/`reverse`/`durationInFrames`, and uses `measureSpring` for natural duration.
- `delayRender`/`continueRender` maintain a set of handles on `window`; rendering proceeds only when all handles clear. During render, each handle arms a timeout (`timeoutInMilliseconds`, default 30s, minus a 2s safety margin) and supports `retries`.
- `staticFile` rejects 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.options` in the renderer's client. The CLI flags, the `Config.*` 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.md` notes the terms change in Remotion 5.0 — track this for compliance and update `setPublicLicenseKey` workflows accordingly.
- **Deprecated-but-live APIs.** `startFrom`/`endAt` (→ `trimBefore`/`trimAfter`), grouped `Config.*` namespaces, `setQuality`/`setImageFormat`/`setLevel`/`setTimeoutInMilliseconds`, and `setPort` remain 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

```bash
# 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.

---

_Generated June 2026 from remotion-dev/remotion at 26d3e03. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
