Run ffmpeg on Cloudflare Workers by calling it, not bundling it.
Workers run in a V8 isolate — no filesystem, no native binaries, hard CPU limits. Your Worker hands Fotovid a URL over HTTPS; the encode runs off-isolate and comes back as a hosted link, so it never counts against your CPU budget.
export default {
async fetch(req: Request, env: { FOTOVID_API_KEY: string }): Promise<Response> {
const { videoUrl, logoUrl } = await req.json();
const res = await fetch("https://api.fotovid.co/v1/video/watermark", {
method: "POST",
headers: {
Authorization: `Bearer ${env.FOTOVID_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
source_url: videoUrl,
params: { type: "image", watermark_image_url: logoUrl, position: "bottom-right" },
}),
});
const { url } = await res.json();
return Response.json({ url });
},
};Why your Worker chokes on ffmpeg
Cloudflare Workers don't run in a container — they run inside a V8 isolate. There's no filesystem to write temp files to, no way to shell out to a native binary, and strict CPU-time limits per request. The ffmpeg binary simply cannot exist there. The WASM build technically loads, but it gets throttled by the isolate's memory and CPU budget and dies on anything past a trivial clip. A hosted API is the only realistic path.
- No filesystem: ffmpeg needs temp files to read input and write output — Workers give you none.
- No native binaries: you can't install or exec the ffmpeg binary in a V8 isolate.
- WASM ffmpeg loads but is capped by isolate memory and CPU limits — it stalls or times out on real video.
- Strict per-request CPU budget makes any transcode-length job a non-starter.
Ways teams try to run ffmpeg on Cloudflare Workers
Before Fotovid these were the options — each one is work you'd rather not own.
| Option | What it costs you | Verdict |
|---|---|---|
| Bundle ffmpeg into your Cloudflare Workers function | Workers can't run native binaries at all | |
| Self-host a container or VM | Always-on cost plus patching, scaling, and monitoring | |
| Run a job queue + worker | New infra: a queue, workers, retries, dead-letter handling | |
| Transcode in the browser (WASM) | Slow, memory-hungry, and crashes on mobile | |
| Call the Fotovid API | One HTTPS request; nothing to run, package, or scale |
Your first call in three steps
- 1Grab a free API key
Sign up and create a key. You'll get an Authorization: Bearer p6_<key_id>:<secret> token that authenticates every call to https://api.fotovid.co. No infrastructure, no ffmpeg build — just a header.
- 2POST your video from the Worker
Call POST /v1/video/watermark with the standard envelope: a source_url pointing to your input video's HTTPS URL, and a params object — set type to image, text, or combo, plus position (e.g. bottom-right), opacity, and scale. It's a plain fetch from inside your Worker, so it fits the isolate's request model perfectly.
- 3Use the hosted result URL
Fotovid returns flat JSON: { id, type, url, expires_at, duration }. The url points to your finished, watermarked video on Fotovid's hosting. Hand it straight to your client, or copy it into your own bucket to keep a permanent copy — the hosted result is temporary.
Frequently asked questions
Ship it with one API call
Grab a free key and make your first call in under five minutes.
