ffmpeg-cloudflare-containers
Complete Cloudflare Container FFmpeg system. PROACTIVELY activate for: (1) Cloudflare Containers setup, (2) Native FFmpeg at edge, (3) GPU-accelerated containers, (4) Durable Objects integration, (5) R2 storage for video files, (6) Container autoscaling, (7) Streaming large files, (8) Workers + Containers architecture, (9) Live streaming relay at edge, (10) Container vs Workers comparison. Provides: Dockerfile examples, Worker code, container configuration, GPU setup, R2 integration, production patterns. Ensures: Native FFmpeg performance at Cloudflare edge with full GPU support.
What this skill does
## Quick Reference
| Component | Configuration |
|-----------|---------------|
| Container Class | `export class FFmpegContainer extends Container` |
| Dockerfile | `FROM jrottenberg/ffmpeg:7.1-alpine` |
| wrangler.toml | `[[containers]]` with `class_name` and `image` |
| Feature | Workers (ffmpeg.wasm) | Containers (Native) |
|---------|----------------------|---------------------|
| Size Limit | 10MB | Unlimited |
| Performance | 10-100x slower | Native speed |
| GPU Support | None | NVIDIA available |
| Cold Start | Instant | 2-3 seconds |
## When to Use This Skill
Use for **edge video processing at scale**:
- Native FFmpeg performance at Cloudflare edge
- GPU-accelerated transcoding in containers
- Large file processing (>10MB)
- Complex FFmpeg pipelines
- R2 storage integration for video files
---
# FFmpeg in Cloudflare Containers (2025)
## Overview
Cloudflare Containers (launched June 2025) enable running FFmpeg natively in a full Linux container environment at the edge. This overcomes all limitations of Workers-based approaches (ffmpeg.wasm size limits, SharedArrayBuffer restrictions).
## Key Benefits
| Feature | Workers (ffmpeg.wasm) | Containers (Native) |
|---------|----------------------|---------------------|
| Binary Size | Limited to 10MB | Unlimited (disk space) |
| Performance | WebAssembly overhead | Native speed |
| GPU Support | None | NVIDIA GPUs available |
| Filesystem | Virtual/memory only | Full Linux FS |
| Dependencies | Must compile to WASM | Any Linux package |
| Cold Start | Instant | 2-3 seconds |
## Getting Started
### Prerequisites
```bash
# Docker must be running locally
docker info
# Verify wrangler installation
npx wrangler --version
```
### Create Container Project
```bash
# Create from template
npm create cloudflare@latest -- --template=cloudflare/templates/containers-template
# Or manually configure existing project
```
### wrangler.toml Configuration
```toml
name = "ffmpeg-processor"
main = "src/index.ts"
compatibility_date = "2025-12-19"
# Container configuration
[[containers]]
class_name = "FFmpegContainer"
image = "./Dockerfile"
max_instances = 10
# Durable Object binding for container
[[durable_objects.bindings]]
name = "FFMPEG_CONTAINER"
class_name = "FFmpegContainer"
# Required migration for SQLite-backed containers
[[migrations]]
tag = "v1"
new_sqlite_classes = ["FFmpegContainer"]
```
### Dockerfile for FFmpeg
```dockerfile
# Use official FFmpeg image
FROM jrottenberg/ffmpeg:7.1-alpine AS ffmpeg
# Production image
FROM node:22-alpine
# Install FFmpeg from the official image
COPY --from=ffmpeg /usr/local /usr/local
# Install additional dependencies
RUN apk add --no-cache \
libva \
libva-utils \
mesa-va-gallium
# Set up application
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# Container must listen on a port
EXPOSE 8080
CMD ["node", "server.js"]
```
### Worker Code
```typescript
// src/index.ts
import { Container, DurableObject } from "cloudflare:workers";
interface Env {
FFMPEG_CONTAINER: DurableObjectNamespace<FFmpegContainer>;
}
export class FFmpegContainer extends Container {
// Default port the container listens on
defaultPort = 8080;
// Sleep after 5 minutes of inactivity
sleepAfter = "5m";
// Environment variables for container
envVars = {
NODE_ENV: "production",
FFMPEG_PATH: "/usr/local/bin/ffmpeg"
};
async onStart() {
console.log("FFmpeg container started");
}
async onStop() {
console.log("FFmpeg container stopped");
}
async onError(error: Error) {
console.error("Container error:", error);
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Route to container for video processing
if (url.pathname.startsWith("/process")) {
// Create unique container instance per job
const id = env.FFMPEG_CONTAINER.idFromName(crypto.randomUUID());
const container = env.FFMPEG_CONTAINER.get(id);
// Forward request to container
return container.fetch(request);
}
// Load balance across containers
if (url.pathname === "/lb") {
const id = env.FFMPEG_CONTAINER.newUniqueId();
const container = env.FFMPEG_CONTAINER.get(id);
return container.fetch(request);
}
return new Response("FFmpeg Container Service", { status: 200 });
}
};
```
### Container Server (Node.js)
```javascript
// server.js - Runs inside the container
import express from 'express';
import { spawn } from 'child_process';
import fs from 'fs/promises';
import path from 'path';
const app = express();
app.use(express.raw({ type: '*/*', limit: '100mb' }));
const TEMP_DIR = '/tmp/ffmpeg-work';
app.post('/transcode', async (req, res) => {
const jobId = Date.now().toString();
const workDir = path.join(TEMP_DIR, jobId);
await fs.mkdir(workDir, { recursive: true });
try {
// Write input file
const inputPath = path.join(workDir, 'input');
await fs.writeFile(inputPath, req.body);
// Get output format from query
const format = req.query.format || 'mp4';
const outputPath = path.join(workDir, `output.${format}`);
// Run FFmpeg
const result = await runFFmpeg([
'-i', inputPath,
'-c:v', 'libx264',
'-preset', 'veryfast',
'-crf', '23',
'-c:a', 'aac',
'-b:a', '128k',
'-movflags', '+faststart',
outputPath
]);
if (result.exitCode !== 0) {
throw new Error(result.stderr);
}
// Return processed file
const output = await fs.readFile(outputPath);
res.set('Content-Type', `video/${format}`);
res.send(output);
} finally {
// Cleanup
await fs.rm(workDir, { recursive: true, force: true });
}
});
app.post('/gif', async (req, res) => {
const jobId = Date.now().toString();
const workDir = path.join(TEMP_DIR, jobId);
await fs.mkdir(workDir, { recursive: true });
try {
const inputPath = path.join(workDir, 'input');
const palettePath = path.join(workDir, 'palette.png');
const outputPath = path.join(workDir, 'output.gif');
await fs.writeFile(inputPath, req.body);
// Two-pass GIF for better quality
// Pass 1: Generate palette
await runFFmpeg([
'-i', inputPath,
'-vf', 'fps=15,scale=480:-1:flags=lanczos,palettegen',
'-y', palettePath
]);
// Pass 2: Generate GIF with palette
await runFFmpeg([
'-i', inputPath,
'-i', palettePath,
'-lavfi', 'fps=15,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse',
'-y', outputPath
]);
const output = await fs.readFile(outputPath);
res.set('Content-Type', 'image/gif');
res.send(output);
} finally {
await fs.rm(workDir, { recursive: true, force: true });
}
});
app.get('/health', (req, res) => {
res.json({ status: 'healthy', ffmpeg: process.env.FFMPEG_PATH });
});
function runFFmpeg(args) {
return new Promise((resolve) => {
const proc = spawn('ffmpeg', args, { stdio: ['pipe', 'pipe', 'pipe'] });
let stdout = '';
let stderr = '';
proc.stdout.on('data', (data) => { stdout += data; });
proc.stderr.on('data', (data) => { stderr += data; });
proc.on('close', (exitCode) => {
resolve({ exitCode, stdout, stderr });
});
});
}
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`FFmpeg server listening on port ${PORT}`);
});
```
## GPU-Accelerated Containers
Cloudflare Containers support NVIDIA GPUs for hardware-accelerated encoding:
### GPU Container Configuration
```toml
[[containers]]
class_name = "FFmpegGPU"
image = "./Dockerfile.gpu"
max_instances = 5
# GRelated in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.