FFmpeg jobs for serverless

Run FFmpeg on Vercel

This is the whole integration. One HTTP request offloads the command and returns right away, so your function stays tiny and runs on Node or Edge.

FFmpeg API guide Updated June 2026
// app/api/transcode/route.ts
import { createClient } from "@rendobar/sdk";
export const runtime = "nodejs"; // or "edge", it's a plain fetch
const rb = createClient({ apiKey: process.env.RENDOBAR_API_KEY! });
export async function POST(req: Request) {
const { videoUrl } = await req.json();
// Offload the command, return right away.
const job = await rb.jobs.create({
type: "raw.ffmpeg",
params: { command: `ffmpeg -i ${videoUrl} -c:v libx264 out.mp4` },
});
return Response.json({ jobId: job.id });
}

Why the local approach breaks

You can't run a standard FFmpeg binary inside a Vercel Function. The binary plus codecs is large enough to push the unzipped bundle toward the 250 MB Lambda cap, and the Edge runtime has no child_process to spawn it with.

This is what people try first. It only deploys on Node, carries around 80 MB of static binary, and holds the function open for the whole transcode. The offload version is the snippet at the top of this page.

Bundle the binary Breaks on Vercel
// app/api/transcode/route.ts
import ffmpegPath from "ffmpeg-static";
import { spawn } from "node:child_process";
// Adds around 80 MB of binary, pushing toward the 250 MB cap.
// And on the Edge runtime this file won't even deploy:
// there is no child_process to spawn a binary with.
export const runtime = "nodejs"; // Edge is off the table
export async function POST(req: Request) {
const { videoUrl } = await req.json();
return new Promise((resolve) => {
const proc = spawn(ffmpegPath!, [
"-i", videoUrl, "-c:v", "libx264", "-crf", "23", "/tmp/out.mp4",
]);
// The function holds open for the whole transcode and can hit
// the 300s wall (800s on Pro with maxDuration) on long jobs.
proc.on("close", () => resolve(Response.json({ ok: true })));
});
}

What happens after you submit

  1. 01

    Submit the command

    Your Vercel function calls rb.jobs.create with type "raw.ffmpeg" and your command string. The call is a single HTTPS request, so it returns in milliseconds and your function stays small.

  2. 02

    A sandboxed container runs it

    Rendobar runs the exact FFmpeg command in an isolated container built for media work. Full codecs, full CPU, no 250 MB bundle, no Lambda layer to attach.

  3. 03

    The result is stored, a URL comes back

    When the command finishes, the output is stored and the job result carries its URL. Read it from the webhook in the next step, or poll GET /jobs/{id}.

  4. 04

    A webhook fires

    Rendobar POSTs the finished job to the endpoint you registered. Your function never had to wait, so it never hit the 300s timeout (or the 800s Pro ceiling).

Ways to run FFmpeg on Vercel, compared

ApproachWorks on Vercel?Why
ffmpeg-static in the bundle NoBlows the 250 MB cap, Node runtime only
ffmpeg.wasm in the function NoRuns, but slow and memory / time limited
Custom Lambda layer NoYou don't control Vercel's Lambda
Rendobar API (offload) YesOne fetch, any runtime, nothing to bundle

Frequently asked questions

Why can't I run FFmpeg on Vercel directly?

Vercel Functions deploy to AWS Lambda with a 250 MB unzipped bundle cap, and a static FFmpeg build plus codecs pushes most projects over it. The Edge runtime has no child_process to spawn a binary at all. Offloading the command keeps your function small.

Does this work with the Edge runtime?

Yes. The call is a plain HTTPS fetch, so it runs on both Node and Edge. Rendobar runs the FFmpeg command in a sandboxed container and returns the output URL on a webhook.

My function times out before the video is done. How do I get the result?

Don't keep the function open. Register a webhook endpoint once (rb.webhooks.create) and Rendobar POSTs each finished job to it. You can also poll GET /jobs/{id} or await rb.jobs.wait(id) for short jobs.

I set maxDuration in vercel.json. Doesn't that fix it?

It raises the ceiling to 800s on Pro (Hobby stays at 300s), but a long transcode can still blow past it, and you pay for every second the function stays open. Offloading returns in milliseconds no matter how long the video runs.

What's the largest command I can send?

Any single FFmpeg command. You pass it as params.command on a raw.ffmpeg job, exactly the string you'd type in a terminal. Inputs are fetched by URL, up to 100 MB on the Free plan and 2 GB on Pro.

What does it cost?

Jobs are billed by compute time. Every account begins with $5 in free credits and no credit card, so you can run a real transcode before paying anything.

The call is a plain HTTPS request, so the same offload runs from every serverless host. Pick your runtime and follow the guide.

Ship FFmpeg on Vercel today

$5 free on signup. No credit card. No bundle to blow.