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.
| Option | What it costs you | Verdict |
|---|---|---|
| Bundle ffmpeg into your Deno Deploy function | No subprocess to launch the ffmpeg binary | |
| 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 — 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.
- 2POST 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.
- 3Use 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.
