VideoFlowcodeGitHubTry itCoreRenderersReact Video EditorPlaygroundExamplesDocscodeGitHubTry it
← Back to Blog

How to Build a Production-Ready Video Rendering Pipeline with Node.js and BullMQ

May 21, 2026 · By VideoFlowLearn how to scale VideoFlow rendering using BullMQ and Node.js. Build a robust, distributed video rendering pipeline that handles high-concurrency workloads.How to Build a Production-Ready Video Rendering Pipeline with Node.js and BullMQ

Building a video rendering pipeline that can handle hundreds of concurrent requests isn't just about how fast you can render a single frame. It’s about how you manage resources, handle failures, and scale your worker nodes without crashing your primary application server.

When you're automating video at scale—whether for personalized SaaS recaps or dynamic social media ads—you need a distributed architecture. In this guide, we’ll show you how to build a production-ready video rendering pipeline using VideoFlow, Node.js, and BullMQ.

Why a Queue Matters for Video Automation

Video rendering is a CPU and memory-intensive task. If you try to render a complex 1080p video directly inside your web request handler, you'll quickly block the event loop and exhaust your server's resources.

A robust video rendering pipeline solves this by:

  1. Decoupling the request from the execution.
  2. Isolating the rendering environment in worker nodes.
  3. Providing automatic retries and progress tracking.

By using BullMQ, we can create a resilient system where our main API simply emits a "render job" and a fleet of workers handles the heavy lifting.

Worker Node Visualization

Setting Up the VideoFlow Worker

First, let's look at the worker code. We'll use @videoflow/renderer-server because it runs in Node.js and leverages headless Chromium via Playwright for high-fidelity rendering.

import { Worker } from 'bullmq';
import VideoFlow from '@videoflow/core';
import '@videoflow/renderer-server';

const worker = new Worker('video-tasks', async (job) => {
  const { videoData, outputKey } = job.data;

  // 1. Reconstruct the VideoFlow instance from JSON
  // In a real app, you might generate this dynamically from job.data
  const $ = new VideoFlow({
    width: 1920,
    height: 1080,
    fps: 30
  });

  $.addText({ 
    text: `Hello, ${videoData.userName}`,
    fontSize: 6,
    color: '#FF5A1F',
    position: [0.5, 0.5]
  }).fadeIn('500ms');

  $.wait('3s');

  // 2. Render to a Buffer
  // This uses the official @videoflow/renderer-server package
  const buffer = await $.renderVideo({
    outputType: 'buffer',
    verbose: false
  });

  // 3. Upload to S3 or your storage provider
  await uploadToS3(outputKey, buffer);

  return { success: true, url: `https://cdn.example.com/${outputKey}` };
}, {
  connection: { host: 'localhost', port: 6379 },
  concurrency: 2 // Adjust based on your CPU/RAM
});

Managing Concurrency and Resources

One of the biggest challenges in a video rendering pipeline is resource management. Each instance of @videoflow/renderer-server launches a headless browser. While VideoFlow is highly optimized, multiple concurrent renders can still eat through RAM.

Concurrency Visualization

When configuring your workers, consider these three rules:

  • Concurrency Limits: Start with a low concurrency (1-2) per worker and scale horizontally by adding more worker processes/containers rather than increasing concurrency on a single machine.
  • Timeout Management: Video rendering can take time. Ensure your BullMQ jobs have a generous lockDuration and timeout to prevent jobs from being marked as failed while they are still processing.
  • Graceful Shutdowns: Always call closeSharedBrowser() from @videoflow/renderer-server when your worker process receives a termination signal to ensure no zombie Chromium processes are left behind.

How VideoFlow Simplifies the Pipeline

VideoFlow was built from the ground up to be "pipeline-friendly." Unlike other tools that require a specific framework or a proprietary runtime, VideoFlow uses a portable VideoJSON format.

You can prototype your scenes in the VideoFlow Playground, export the JSON, and then feed that exact same JSON into your server-side workers. Because our official renderers produce byte-for-byte identical output, you never have to worry about a video looking different on your server than it did during development.

This portability is what makes it a superior choice for automated SaaS user recap videos and other high-volume automation tasks. You can even use the browser-based renderer to offload the rendering cost to the user's browser when a server-side queue isn't necessary.

Scaling to Thousands of Videos

As your needs grow, you can deploy your workers to serverless environments or container orchestration platforms like Kubernetes. Since @videoflow/renderer-server doesn't require FFmpeg by default (it uses a high-performance WebCodecs-based pipeline), your container images stay lean and easy to deploy.

Ready to start building? Check out our Getting Started guide or dive into the GitHub repository to see the source code for our renderers. Your production-ready video rendering pipeline is just a few lines of code away.

VideoFlow

Open-source toolkit for composing videos from code.

Product

CoreRenderersReact Video EditorPlayground

Learn

DocsAPI referenceExamplesvs. Remotionvs. FFmpeg

Project

GitHubLicenseContactTermsPrivacy

From the blog

All posts →How to Automate Loom-style Product Demos with TypeScriptAutomated Podcast Audiogram Generator: Turning Audio into Viral Video with TypeScriptAutomating Personalized Onboarding Videos with VideoFlow and TypeScriptAutomating YouTube Shorts: Build a Vertical Video Factory in 30 Lines of TypeScriptCinematic 3D Video with TypeScript: A Guide to Perspective and RotationCinematic GLSL Effect Stacking: Building High-End Visuals with CodeHeadless Video Rendering in Node.js: No FFmpeg RequiredVideo as Data: Building an LLM-Powered Video Generation Pipeline
© 2026 VideoFlow. Apache-2.0 core.