ffmpeg on Deno Deploy

Why can't ffmpeg run on Deno Deploy? No subprocess — so call the API.

Deno Deploy runs your code in V8 isolates with no subprocess and no FFI, so the ffmpeg binary can't launch. Call Fotovid over fetch() instead — it runs the encode elsewhere and answers with a plain https link to the file.

Deno.serve(async (req) => {
  const { videoUrl, logoUrl } = await req.json();

  const res = await fetch("https://api.fotovid.co/v1/video/watermark", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${Deno.env.get("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 });
});

The isolate model won't let ffmpeg run — at all

Deno Deploy is built on V8 isolates for fast edge logic, not CPU-heavy media encoding. There's no subprocess API to spawn a native binary and no FFI to load one, so the ffmpeg process never starts. This isn't a config flag or a permission you can grant — the primitives ffmpeg needs simply don't exist in the isolate sandbox. WebAssembly ports of ffmpeg exist, but they blow past isolate CPU and memory limits on any real video and can't stream a large file through.

  • No subprocess: Deno.Command and child_process can't launch the ffmpeg binary inside an isolate.
  • No FFI: you can't dlopen a native ffmpeg library and call into it directly.
  • Wasm ffmpeg exhausts isolate CPU/memory on real videos and can't stream large inputs.
  • The platform is tuned for short edge requests, not multi-second encode jobs.

Ways teams try to run ffmpeg on Deno Deploy

Before Fotovid these were the options — each one is work you'd rather not own.

OptionWhat it costs youVerdict
Bundle ffmpeg into your Deno Deploy functionNo subprocess to launch the ffmpeg binary
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 — no card to start. You get a key ID and secret that combine into a Bearer token of the form p6_<key_id>:<secret>. Store it in a Deno Deploy environment variable so your isolate can authenticate without shipping any binary.

  2. 2
    POST to the watermark endpoint

    From your Deno handler, make a normal fetch to POST https://api.fotovid.co/v1/video/watermark with your Bearer token. Send the standard envelope: a source_url pointing at the HTTPS URL of your input video, plus a params object describing the watermark — type (image, text, or combo), the watermark_image_url or text, position such as bottom-right, opacity, and scale. Fotovid pulls the source, runs ffmpeg on its own infrastructure, and returns before your isolate times out.

  3. 3
    Use the returned URL

    The response is flat JSON: an id, a type, and a url pointing at the finished, Fotovid-hosted video, plus expires_at and duration. Hand that url straight to your client, or fetch it server-side and store your own copy in whatever bucket you already use. Most operations cost 1 credit, so a watermark call is a single credit against your balance.

Frequently asked questions

Ship it with one API call

Grab a free key and make your first call in under five minutes.