Remotion Lambda — Plan de Implementacion

⚡ Tallaje: M

Migrar el render de Remotion del VPS local a AWS Lambda. El VPS orquesta, Lambda renderiza. Efectos futuros sin cambios de infra.

0 / 4 fases
Servidor Hetzner 4CPU / 8GB / 150GB
Remotion version 4.0.434
Composiciones usadas ContentCards, MotionGraphic
Render actual (13 clips) ~60 min (swap activo)
Render esperado Lambda ~1-2 min
Costo estimado $1-5 / mes
VPS Hetzner (orquestador) AWS ┌────────────────────────────┐ ┌───────────────────────────┐1. Whisper transcribe │ │ │ │ 2. Claude visual prompts │ │ Remotion Lambda │ │ 3. Trigger render ─────────┼── API ──▶│ N funciones en paralelo │ │ │ │ Cada frame = 1 Lambda │ │ 4. Download MP4 ◀──────────┼── S3 ◀──│ Output → S3 bucket │ │ 5. FFmpeg split-screen │ │ │ │ 6. Concat + ASS subs │ │ │ └────────────────────────────┘ └───────────────────────────┘
0
Pre-requisitos AWS S
0.1 — Configurar AWS CLI en el VPS
El VPS necesita poder hablar con AWS. Configurar credenciales.
AWS CLI ya esta instalado pero NO configurado. Necesitas Access Key ID y Secret Access Key de tu cuenta AWS.
# En la consola AWS (browser): # IAM → Users → Create user → "remotion-lambda" # Attach policy: AdministratorAccess (temporal, para setup) # Create access key → tipo "CLI" # En el VPS: aws configure # AWS Access Key ID: [pegar] # AWS Secret Access Key: [pegar] # Default region: us-east-1 # Default output format: json
0.2 — Verificar acceso
aws sts get-caller-identity # Debe mostrar el ARN del usuario remotion-lambda
1
Setup Remotion Lambda en AWS M
1.1 — Instalar paquetes de Remotion Lambda
Agregar @remotion/lambda al proyecto Remotion existente.
cd ~/thalx-web-app/remotion npm install @remotion/lambda@4.0.434 # IMPORTANTE: misma version que el resto (4.0.434)
1.2 — Crear IAM policy para Remotion Lambda
Remotion necesita permisos especificos en AWS. Reemplazar la policy generica por una minima.
cd ~/thalx-web-app/remotion npx remotion lambda policies user # Esto imprime la IAM policy JSON que necesita el usuario. # Copiar el JSON output.
Luego en la consola AWS (browser):
# IAM → Users → remotion-lambda → Permissions # 1. Remove AdministratorAccess # 2. Add inline policy → JSON tab → pegar el output de arriba # 3. Name: "remotion-lambda-policy" # 4. Create policy
1.3 — Validar permisos
cd ~/thalx-web-app/remotion npx remotion lambda policies validate # Debe decir "All policies are valid"
1.4 — Deployar funcion Lambda
Esto crea la funcion Lambda en AWS con Chromium incluido (~50MB).
cd ~/thalx-web-app/remotion npx remotion lambda functions deploy --memory=2048 --timeout=240 --disk=2048 # Output esperado: # Function name: remotion-render-4-0-434-mem2048mb-disk2048mb-240sec # Guardar este nombre → se usa en el paso 2
--memory=2048: 2GB RAM por Lambda. Suficiente para ContentCards/MotionGraphic. Si renders futuros mas complejos → subir a 3072.
--timeout=240: 4 min max por render. Clips de <15s rinden en <30s.
--disk=2048: 2GB disco temporal para el video output.
1.5 — Crear site (subir bundle a S3)
Esto empaqueta tu codigo Remotion (React components) y lo sube a S3 para que Lambda pueda usarlo.
cd ~/thalx-web-app/remotion npx remotion lambda sites create src/index.ts --site-name=thalx-remotion # Output esperado: # Site: thalx-remotion # Bucket: remotionlambda-useast1-XXXXX # Serve URL: https://remotionlambda-useast1-XXXXX.s3.us-east-1.amazonaws.com/sites/thalx-remotion/index.html # # Guardar Serve URL → se usa en el paso 2
Cada vez que modifiques las composiciones (ContentCards, MotionGraphic, etc.), re-ejecutar este comando para actualizar el bundle en S3.
1.6 — Test rapido desde CLI
Verificar que Lambda funciona renderizando un clip de prueba.
cd ~/thalx-web-app/remotion npx remotion lambda render thalx-remotion ContentCards \ --props='{"heading":"TEST","bullets":["Primer bullet","Segundo bullet"],"bgColor":"#0d9488","accentColor":"#5eead4","duration":5}' \ --out=/tmp/test-lambda-render.mp4 \ --log=verbose # Debe completar en ~10-30 segundos # Verificar: ls -la /tmp/test-lambda-render.mp4
2
Adaptar render_splitscreen.py M
Que cambia: Solo el Step 3 (render_motion_clips). Steps 1, 2, 4, 5, 6 quedan IDENTICOS.
Que NO cambia: Las composiciones React, los estilos, los efectos. Solo donde se renderizan.
2.1 — Crear script auxiliar: lambda_render.mjs
Script Node.js que invoca Remotion Lambda via SDK (reemplaza render_motion_clip.mjs para Lambda).
📄 ~/thalx-web-app/remotion/lambda_render.mjs
// lambda_render.mjs — Render a Remotion composition via AWS Lambda // Usage: node lambda_render.mjs '{"compositionId":"ContentCards",...}' // Output: JSON to stdout with {video_path, duration, size_bytes} import { renderMediaOnLambda, getRenderProgress, downloadMedia, } from "@remotion/lambda/client"; import fs from "fs"; // ── Config: ACTUALIZAR con valores del paso 1.4 y 1.5 ── const FUNCTION_NAME = process.env.REMOTION_FUNCTION_NAME || "remotion-render-4-0-434-mem2048mb-disk2048mb-240sec"; const SERVE_URL = process.env.REMOTION_SERVE_URL || "https://remotionlambda-useast1-XXXXX.s3.us-east-1.amazonaws.com/sites/thalx-remotion/index.html"; const REGION = "us-east-1"; const config = JSON.parse(process.argv[2]); const { compositionId = "ContentCards", outputPath, duration = 5, width = 768, height = 1080, ...restProps } = config; const fps = 30; const durationInFrames = Math.ceil(duration * fps); // 1. Trigger render on Lambda const { renderId, bucketName } = await renderMediaOnLambda({ region: REGION, functionName: FUNCTION_NAME, serveUrl: SERVE_URL, composition: compositionId, codec: "h264", inputProps: { ...restProps, duration }, framesPerLambda: 20, imageFormat: "jpeg", maxRetries: 1, privacy: "private", downloadBehavior: { type: "play-in-browser" }, }); console.error(`[lambda] Render started: ${renderId}`); // 2. Poll progress let progress; while (true) { progress = await getRenderProgress({ renderId, bucketName, functionName: FUNCTION_NAME, region: REGION, }); if (progress.done) break; if (progress.fatalErrorEncountered) { console.error(`[lambda] FATAL: ${JSON.stringify(progress.errors)}`); process.exit(1); } const pct = ((progress.overallProgress || 0) * 100).toFixed(0); console.error(`[lambda] Progress: ${pct}%`); await new Promise(r => setTimeout(r, 1000)); } // 3. Download to local path const { outputFile } = await downloadMedia({ bucketName, renderId, region: REGION, outPath: outputPath, }); const stat = fs.statSync(outputFile); const result = { video_path: outputFile, duration, width, height, size_bytes: stat.size, }; console.log(JSON.stringify(result));
2.2 — Modificar render_splitscreen.py → Step 3
Reemplazar la funcion render_motion_clips para que llame a lambda_render.mjs en vez de render_motion_clip.mjs.
📄 ~/thalx-web-app/backend/scripts/render_splitscreen.py — MODIFICAR funcion render_motion_clips (linea ~200)
# ── Step 3: Render via Remotion Lambda (REEMPLAZA version local) ── # Cambiar REMOTION_SCRIPT de render_motion_clip.mjs → lambda_render.mjs REMOTION_DIR = Path.home() / "thalx-web-app" / "remotion" REMOTION_SCRIPT = REMOTION_DIR / "lambda_render.mjs" # ← CAMBIO CLAVE # En la funcion render_motion_clips: # 1. Cambiar Semaphore(2) → Semaphore(6) (Lambda soporta mas paralelos) # 2. Cambiar el subprocess de "node render_motion_clip.mjs" → "node lambda_render.mjs" # 3. El formato JSON de input NO cambia (misma estructura) async def render_motion_clips( transcriptions: list[dict], prompts: list[dict], durations: list[float], work_dir: Path, ) -> list[Path]: """Render clips via Remotion Lambda (parallel, no local resources).""" sem = asyncio.Semaphore(6) # ← era 2, ahora 6 (Lambda no usa RAM local) # ... el resto de _render_one queda IGUAL ... # Solo cambia la referencia a REMOTION_SCRIPT (ya definida arriba) # y el semaphore de 2 → 6
Cambios minimos: Solo 2 lineas cambian en render_splitscreen.py:
1. REMOTION_SCRIPT = ... / "lambda_render.mjs"
2. Semaphore(2)Semaphore(6)
Todo lo demas (transcripcion, prompts, FFmpeg compose, subs) queda intacto.
2.3 — Variables de entorno
Guardar los valores de Lambda en ~/.env para que el script los use.
# Agregar a ~/.env: REMOTION_FUNCTION_NAME=remotion-render-4-0-434-mem2048mb-disk2048mb-240sec REMOTION_SERVE_URL=https://remotionlambda-useast1-XXXXX.s3.us-east-1.amazonaws.com/sites/thalx-remotion/index.html AWS_REGION=us-east-1 # (reemplazar XXXXX con el valor real del paso 1.5)
2.4 — Fallback local (opcional pero recomendado)
Si Lambda falla, que el script caiga al render local automaticamente.
📄 Logica de fallback en render_splitscreen.py → _render_one()
# Dentro de _render_one, despues del subprocess de lambda_render.mjs: if proc.returncode != 0: logger.warning("[lambda] Scene %d failed, falling back to local render", idx) # Fallback: render local (como antes) proc_local = await asyncio.create_subprocess_exec( "node", str(REMOTION_DIR / "render_motion_clip.mjs"), config, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=str(REMOTION_DIR), ) # ... procesar resultado local ...
3
Test end-to-end + Cleanup S
3.1 — Test con 1 clip
# Crear directorio de test con 1 solo clip mkdir /tmp/test-lambda-pipeline cp ~/uploads/PrimerVideoAI4Man/clip1.mp4 /tmp/test-lambda-pipeline/ # Correr pipeline completo cd ~/thalx-web-app/backend ~/agents-claude-env/bin/python3 scripts/render_splitscreen.py \ /tmp/test-lambda-pipeline/ \ /tmp/test-lambda-output.mp4 # Debe completar en ~2-3 min (vs ~10 min antes) # Verificar output: ls -la /tmp/test-lambda-output.mp4 ffprobe /tmp/test-lambda-output.mp4 2>&1 | grep Duration
3.2 — Test completo (13 clips)
cd ~/thalx-web-app/backend ~/agents-claude-env/bin/python3 scripts/render_splitscreen.py \ ~/uploads/PrimerVideoAI4Man/ \ ~/playgrounds/miles/vsl_ai4managers_splitscreen_v5.mp4 # Target: <5 min total (vs ~60 min antes) # Verificar visual: abrir en playgrounds.digitalhubassist.ai/miles/
3.3 — Restringir permisos IAM
Ahora que funciona, quitar AdministratorAccess si no se hizo en 1.2.
# En la consola AWS: # IAM → Users → remotion-lambda → Permissions # Verificar que SOLO tiene "remotion-lambda-policy" # Si aun tiene AdministratorAccess → Remove
3.4 — Verificar costos
# Verificar costos despues del test: # AWS Console → Billing → Cost Explorer # Filtrar por servicio: Lambda, S3 # El test de 13 clips debe costar <$0.50

Antes vs Despues

Metrica Antes (local) Despues (Lambda)
Render 13 clips motion ~26 min ~1-2 min
Pipeline completo ~60 min ~5-8 min
RAM usada en VPS 1.2 GB + swap ~200 MB (solo FFmpeg)
Costo/mes $0 (pero mata el server) $1-5/mes
Efectos nuevos Mismo React/Remotion Mismo React/Remotion
Archivos modificados 1 nuevo (lambda_render.mjs) + 2 lineas en render_splitscreen.py
Para renders futuros con nuevos efectos: solo necesitas (1) actualizar las composiciones React como siempre, (2) re-ejecutar npx remotion lambda sites create src/index.ts --site-name=thalx-remotion para subir el nuevo bundle. Lambda usa el ultimo bundle de S3 automaticamente.