A media file converter that runs ffmpeg inside a Vercel Sandbox VM, orchestrated by Workflow SDK. Demonstrates the webhook callback pattern — the workflow suspends with zero compute while the Sandbox does the work, then resumes when ffmpeg completes and curls the webhook URL.
- User provides a media file URL and selects an output format
- The workflow creates a Sandbox VM and installs ffmpeg (as durable steps with visible stdout/stderr)
- The input file is downloaded and its metadata collected
- A webhook is created, and a shell script runs ffmpeg in the background
- The workflow suspends — zero compute while the Sandbox does the conversion
- When ffmpeg finishes, it
curls the webhook URL, resuming the workflow - The workflow collects the output metadata and cleans up the Sandbox
export async function convertMedia(baseUrl: string, inputUrl: string, outputFormat: string) {
"use workflow";
// Sandbox.create() is a durable step — the instance is automatically
// serialized across step boundaries via WORKFLOW_SERIALIZE
const sandbox = await Sandbox.create({ timeout: 5 * 60 * 1000 });
try {
// Setup steps run synchronously with visible stdout/stderr
await run(sandbox, "sudo", ["dnf", "install", "-y", "xz"]);
await run(sandbox, "bash", ["-c", "curl -sfL '...' | tar xJf - -C /tmp/ffmpeg"]);
await run(sandbox, "bash", ["-c", `curl -sfL -o /tmp/input '${inputUrl}'`]);
// Create webhook — the Sandbox will curl this URL when done
const webhook = createWebhook();
const callbackUrl = new URL(webhook.url, baseUrl).href;
// Write + start the conversion script in the background
await sandbox.writeFiles([{ path: "convert.sh", content: script }]);
await run(sandbox, "bash", ["-c", "bash convert.sh &"]);
// Workflow SUSPENDS — zero compute while ffmpeg runs in the Sandbox.
// When the script finishes, it curls the webhook URL to resume.
const result = await Promise.race([webhook, sleep("5m")]);
const metadata = await result.json();
// ...
} finally {
await sandbox.stop();
}
}- Webhook callback from Sandbox — the Sandbox's shell script
curls the workflow webhook URL when ffmpeg completes, resuming the suspended workflow createWebhook()— generates a unique URL that the Sandbox can POST to- Sandbox in
"use workflow"—Sandbox.create()and all Sandbox methods have"use step"built in, so they work directly in workflow functions. The Sandbox instance is automatically serialized across step boundaries via Workflow SDK's custom class serialization Promise.race([webhook, sleep("5m")])— built-in timeout patterntry/finallywithsandbox.stop()— ensures the Sandbox VM is cleaned up even on failure
- Node.js 18+
- pnpm
- A Vercel account with Sandbox access
git clone https://github.com/vercel-labs/workflow-sandbox-ffmpeg-example.git
cd workflow-sandbox-ffmpeg-example
pnpm installCreate a .env.local file:
VERCEL_TEAM_ID=your_vercel_team_id
VERCEL_PROJECT_ID=your_vercel_project_idpnpm devOpen http://localhost:3000 and provide a URL to a media file to convert.