FFmpeg jobs on R2

Run FFmpeg on files in Cloudflare R2

Keep the uploads in R2, run the FFmpeg command as a job, and let the output land back in the bucket, where serving it to viewers costs nothing in egress.

FFmpeg API guide Updated September 2026
import { createClient } from "@rendobar/sdk";
const rb = createClient({ apiKey: process.env.RENDOBAR_API_KEY });
// Read the upload from R2, deliver a web-ready MP4 back into the same bucket.
const job = await rb.jobs.create({
type: "ffmpeg",
inputs: { source: "storage://assets/uploads/launch.mov" },
params: {
command: "ffmpeg -i source -c:v libx264 -crf 23 -c:a aac -movflags +faststart out.mp4",
},
destinations: ["storage://assets/videos"],
});

Why the Worker can't do it alone

A Worker bound to the bucket reads the upload in one line. Then it needs FFmpeg, and a V8 isolate runs JavaScript and WebAssembly, never a native binary. There is no child_process to spawn one with.

ffmpeg.wasm fails too, because the runtime refuses to compile WebAssembly it fetched. The Worker is the right front door and the wrong place for the encode.

Read, encode, write No encoder in the isolate
// src/index.ts, a Cloudflare Worker with the bucket bound as MEDIA
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const upload = await env.MEDIA.get("uploads/launch.mov");
// There is no ffmpeg binary in the isolate and no child_process to run
// one with, and ffmpeg.wasm can't compile its core here either.
const mp4 = await transcode(await upload.arrayBuffer()); // nothing to write this with
await env.MEDIA.put("videos/launch.mp4", mp4);
return new Response("never gets here");
},
};

From upload to public link in four steps

  1. 01

    Create a token for one bucket

    In R2, create an Account API token with Object Read & Write applied to that bucket only. Paste it into the connect dialog and the endpoint and key pair are derived from it. Admin permissions are more than a connection needs.

  2. 02

    Submit with storage://

    Name the upload as an input and a folder as a destination. Both references are checked against your account when you submit, so a wrong connection ID fails immediately.

  3. 03

    The job reads one object

    The job gets a signed link to that object and nothing else in the bucket, and runs your exact FFmpeg command for up to 1 hour on Free or 9 hours on Pro.

  4. 04

    Delivered, with a public link

    The output lands under the connection's file name pattern. Set the bucket's r2.dev or custom domain as the connection's public URL, and the delivery event carries a link your viewers can load straight away.

Where the output lives decides what serving it costs

Video is read far more often than it is written. List prices for standard storage, as each provider published them on 2026-09-13:

Cloudflare R2Amazon S3, US East (N. Virginia)
Standard storage, per GB-month$0.015$0.023
Serving to the internet, per GBFree$0.09 after 100 GB a month

Sources: Cloudflare R2 pricing and Amazon S3 pricing. Both also bill per request, and S3's egress rate falls after the first 10 TB a month.

Ways to run FFmpeg on R2 files, compared

ApproachRuns real FFmpeg?Why
ffmpeg.wasm inside the Worker NoThe isolate won't compile its core at runtime, and the core fills the bundle
Cloudflare Containers you run YesReal FFmpeg, and you build the image, the queue and the scaling
A server that pulls from R2 YesWorks, and bills while it waits for the next upload
Rendobar with a storage connection YesOne request from the Worker, output delivered back to R2

Frequently asked questions

Can a Cloudflare Worker run FFmpeg on a file in R2?

Not inside the Worker. The isolate has no native binary and no child_process, and ffmpeg.wasm can't compile its core at runtime there. The Worker can submit a job in one request and return, and the job reads the file from R2 and writes the result back.

Does R2 charge to serve the processed video?

No. R2 has no egress fee, so serving the output to viewers costs nothing in bandwidth. Standard storage is $0.015 per GB-month, and operations are billed per million requests.

What permissions does Rendobar need on R2?

An Account API token with Object Read & Write, applied to the one bucket. Rendobar reads, writes, lists and deletes objects in it, and never creates or deletes buckets, so Admin Read & Write grants more than it uses.

Can a delivered file get a public URL?

Yes. Set the bucket's r2.dev subdomain or a custom domain as the connection's public URL, and each delivery reports a link on that host alongside the path it wrote.

How long can a job run?

Up to 1 hour per job on Free and 9 hours on Pro. A single job we measured ran 1,002 seconds and completed.

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 job against your own bucket before paying anything.

Process your R2 video today

$5 free on signup. No credit card. No egress on the result.