On Runtime, you pay for the CPU an encode uses, and nothing for the minutes around it. A 2 vCPU, 4 GiB sandbox with both CPUs encoding costs $0.08 an hour. So ten minutes of full-load x264 work costs about $0.013. The same sandbox waiting for the next upload costs $0.03125 an hour (pricing). Each job runs in its own Firecracker microVM. This matters for media. A malformed file uploaded by a stranger is parsed on a machine that holds nothing else. Ubuntu 24.04 shipped FFmpeg 6.1.1 on 25 October 2026.
Transcode an upload to web-ready MP4
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 3600 });await sbx.exec("sudo apt-get update -qq && sudo apt-get install -y -qq ffmpeg", { check: true, timeoutMs: 600_000,});await sbx.files.upload("./talk.mov", "/workspace/in.mov");await sbx.exec( [ "ffmpeg", "-hide_banner", "-y", "-i", "in.mov", "-c:v", "libx264", "-preset", "medium", "-crf", "23", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", "out.mp4", ], { check: true, timeoutMs: 3_000_000, onStderr: (text) => process.stderr.write(text) },);await sbx.exec(["ffmpeg", "-y", "-ss", "5", "-i", "out.mp4", "-frames:v", "1", "poster.jpg"], { check: true,});await writeFile("out.mp4", await sbx.files.read("/workspace/out.mp4"));await writeFile("poster.jpg", await sbx.files.read("/workspace/poster.jpg"));Pythonimport sysfrom withruntime import Sandboxwith Sandbox.create(disk_mib=8192, timeout_seconds=3600) as sbx: sbx.exec("sudo apt-get update -qq && sudo apt-get install -y -qq ffmpeg", check=True, timeout_ms=600_000) sbx.files.upload("talk.mov", "/workspace/in.mov") sbx.exec(["ffmpeg", "-hide_banner", "-y", "-i", "in.mov", "-c:v", "libx264", "-preset", "medium", "-crf", "23", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "128k", "-movflags", "+faststart", "out.mp4"], check=True, timeout_ms=3_000_000, on_stderr=sys.stderr.write) sbx.exec(["ffmpeg", "-y", "-ss", "5", "-i", "out.mp4", "-frames:v", "1", "poster.jpg"], check=True) sbx.files.download("/workspace/out.mp4", "out.mp4") sbx.files.download("/workspace/poster.jpg", "poster.jpg")- The command is an array, so it runs without a shell. A file name that a user chose cannot inject shell syntax.
- FFmpeg writes progress to stderr.
onStderrstreams it as it happens. - Per FFmpeg's format documentation,
+faststartmoves the MP4's index to the front of the file. A browser can then start playing before the download ends. - Uploads and downloads move in parallel 1 MiB chunks. Each chunk is checked by SHA-256. Transfers resume after a dropped connection. The file size is limited only by the sandbox's disk.
Read a file's streams as JSON
ffprobe comes with the package. Its JSON output gives an agent the codec,
duration, resolution and bitrate, without text parsing:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ image: "ffmpeg" });await sbx.files.upload("./clip.mkv", "/workspace/clip.mkv");const probe = await sbx.exec( ["ffprobe", "-v", "error", "-of", "json", "-show_format", "-show_streams", "clip.mkv"], { check: true },);const info = JSON.parse(probe.stdout) as { format: { duration: string }; streams: { codec_type: string; codec_name: string; width?: number }[];};console.log( info.format.duration, info.streams.map((s) => `${s.codec_type}:${s.codec_name}`),);Encoders in Ubuntu's build
Ubuntu 24.04's libavcodec60 is the library behind its ffmpeg. Per Ubuntu's
package archive on 25 October 2026, it depends on these codec libraries:
| Library | Use it for | Name in ffmpeg |
|---|---|---|
libx264-164 |
H.264 video, for the widest playback | -c:v libx264 |
libx265-199 |
H.265/HEVC video | -c:v libx265 |
libvpx9 |
VP8 and VP9 video for WebM | -c:v libvpx-vp9 |
libsvtav1enc1d1 |
AV1 encoding | -c:v libsvtav1 |
libdav1d7 |
AV1 decoding | (decoder) |
libopus0 |
Opus audio | -c:a libopus |
libmp3lame0 |
MP3 audio | -c:a libmp3lame |
Run ffmpeg -hide_banner -encoders in a sandbox for the full list.
Encodes run on CPUs
Runtime sandboxes have no GPUs. NVENC and other hardware encoders are therefore not available. Every encoder above runs on the CPU. Two ways to go faster:
- Paid sandboxes can ask for more
vcputhan the two allowed without credit. x264 spreads its work across threads. The bill follows the CPU actually used, not the cores reserved. - Many files at the same time: give each its own sandbox. A paid account runs 100 at once, and an account without credit runs four.
TypeScriptimport { Sandbox } from "withruntime";const files = ["a.mov", "b.mov", "c.mov"];await Promise.all( files.map(async (name) => { await using sbx = await Sandbox.create({ image: "ffmpeg", diskMiB: 8192 }); await sbx.files.upload(`./${name}`, `/workspace/${name}`); await sbx.exec(["ffmpeg", "-y", "-i", name, "-c:v", "libx264", "-crf", "23", "out.mp4"], { check: true, timeoutMs: 3_000_000, }); await sbx.files.download("/workspace/out.mp4", `./${name}.mp4`); }),);Start every sandbox with FFmpeg installed
The samples above that say image: "ffmpeg" use this custom image:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "ffmpeg", recipe: { apt: ["ffmpeg"] } });Pythonfrom withruntime import RuntimeRuntime().images.build(name="ffmpeg", recipe={"apt": ["ffmpeg"]})Building is free. Without credit, a build counts toward the free usage. An account without credit stores three images free.
Disk and time
| Limit | What to set |
|---|---|
| Disk | Input, output and FFmpeg itself share it. The default 4 GiB had about 2.5 GiB free on 24 September 2026. 10 GiB is allowed without credit |
| Disk speed | Up to about 1 GB/s when the server is quiet. At least about 40 MB/s when it is busy |
| One command | 60 seconds by default, up to 24 hours with timeoutMs |
| Sandbox lifetime | timeoutSeconds, up to 24 hours. extend changes it, and keepAlive holds the sandbox while your process runs |
For media that comes from users, turn off the sandbox's internet after the install. A crafted file then cannot call out (turn off sandbox internet). To generate images or thumbnails in other formats, see ImageMagick in a sandbox. Document and PDF pipelines are covered in document conversion. Batch jobs across many sandboxes are covered in AI data pipelines.
Sources
- FFmpeg formats documentation (
movflagsandfaststart), https://ffmpeg.org/ffmpeg-formats.html, read 25 October 2026 - Ubuntu 24.04 ffmpeg and libavcodec60 packages, from the noble package index at http://archive.ubuntu.com/ubuntu/dists/noble/universe/binary-amd64/, read 25 October 2026
Facts on this page were checked on 25 October 2026.