ffmpeg on Cloudflare Workers

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.

OptionWhat it costs youVerdict
Bundle ffmpeg into your Cloudflare Workers functionWorkers can't run native binaries at all
Self-host a container or VMAlways-on cost plus patching, scaling, and monitoring
Run a job queue + workerNew infra: a queue, workers, retries, dead-letter handling
Transcode in the browser (WASM)Slow, memory-hungry, and crashes on mobile
Call the Fotovid APIOne HTTPS request; nothing to run, package, or scale
How it works

Your first call in three steps

  1. 1
    Grab 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.

  2. 2
    POST 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.

  3. 3
    Use 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.