ffmpeg on Supabase Edge Functions

ffmpeg on Supabase Edge Functions, offloaded to a single HTTPS call.

Edge Functions run on Deno, so there's no child_process to spawn the ffmpeg binary. Keep your function, offload the encode to an HTTPS call, and get back a finished file URL.

// supabase/functions/watermark/index.ts
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, // a Supabase Storage signed URL works here
      params: { type: "image", watermark_image_url: logoUrl, position: "bottom-right" },
    }),
  });

  const { url } = await res.json();
  return Response.json({ url });
});

Why ffmpeg won't run in your Edge Function

Supabase Edge Functions execute on the Deno runtime, which has no child_process or subprocess API — so there is nothing to spawn the ffmpeg binary with, even if you bundle it. On top of that, the CPU and wall-clock limits are sized for lightweight request logic, not for the seconds-to-minutes of sustained work that media encoding needs. A Storage trigger is a perfect place to *start* a job the moment a file lands, but the actual transcode has to happen somewhere that can run a real encoder.

  • No child_process / subprocess on Deno — you can't exec the ffmpeg binary
  • CPU and wall-clock limits target quick logic, not media encoding
  • Storage triggers are ideal for kicking off a job, but the work must run elsewhere
  • Static ffmpeg builds, WASM, and layers all dead-end against the runtime, not just the size

Ways teams try to run ffmpeg on Supabase

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

OptionWhat it costs youVerdict
Bundle ffmpeg into your Supabase functionDeno has no child_process — you can't spawn the 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. Fotovid runs on usage credits — $1 = 100 credits and most operations cost 1 credit — so you can wire up the flagship watermark call and test it end to end before spending anything.

  2. 2
    Call the watermark endpoint from your function

    From inside the same Edge Function (or the Storage trigger that fired on upload), POST to https://api.fotovid.co/v1/video/watermark with an Authorization: Bearer p6_<key_id>:<secret> header. Send the standard envelope: a source_url pointing at your video's HTTPS URL — a Supabase Storage signed URL works directly — plus a params object describing the overlay, e.g. type image, a watermark_image_url, a position like bottom-right, and an opacity or scale. Your function just makes one HTTPS request; Fotovid runs the ffmpeg work off-platform, so you never touch the Deno CPU or time limits.

  3. 3
    Use the hosted result URL

    The response is flat JSON: an id, a type, and a url pointing at the finished, Fotovid-hosted video. Hand that url straight back to your caller, or download it and store your own copy in Supabase Storage. Treat the hosted URL as temporary — it carries an expires_at — so persist a copy if you need it to live long-term.

Frequently asked questions

Ship it with one API call

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