Scale Video on Demand with FFmpeg in Docker

This guide explains how to use FFmpeg within Docker containers to transcode and scale video on demand (VOD) dynamically. It covers pulling the appropriate Docker image, executing video scaling commands through volume mounts, generating multi-bitrate outputs for adaptive streaming, and architecting an automated, scalable processing pipeline using containerized workers.


Why Run FFmpeg in Docker for VOD?

Using Docker to containerize FFmpeg eliminates dependency conflicts and hardware library mismatches across host environments. For Video on Demand workflows, containerization allows you to spin up independent encoding tasks in parallel, automatically scaling compute resources up or down based on queue volume via orchestrators like Kubernetes, Docker Swarm, or AWS ECS.


Step 1: Select or Build an FFmpeg Docker Image

You can use pre-built, production-ready images such as jrottenberg/ffmpeg or linuxserver/ffmpeg.

To pull a lightweight, production-ready image:

docker pull jrottenberg/ffmpeg:latest

If custom codecs or specific hardware acceleration runtimes (such as NVIDIA NVENC) are required, define a custom Dockerfile:

FROM ubuntu:22.04

RUN apt-get update && \
    apt-get install -y ffmpeg && \
    rm -rf /var/lib/apt/lists/*

ENTRYPOINT ["ffmpeg"]

Step 2: Scale a Video Resolution Using Docker

To process videos stored on your host machine, mount your local directory to a container volume using -v.

Run the following command to scale an input video to 720p (1280x720):

docker run --rm -v $(pwd):/workspace -w /workspace jrottenberg/ffmpeg:latest \
  -i input.mp4 \
  -vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
  -c:v libx264 -crf 23 -preset fast \
  -c:a aac -b:a 128k \
  output_720p.mp4

Flag Explanations:

  • --rm: Automatically removes the container instance after processing finishes.
  • -v $(pwd):/workspace: Mounts the current working directory to /workspace inside the container.
  • -w /workspace: Sets the container working directory to /workspace.
  • -vf "scale=...": Scales the video while maintaining the original aspect ratio and padding the edges with black bars if necessary.
  • -c:v libx264: Uses the H.264 video codec for broad compatibility.
  • -crf 23: Sets the Constant Rate Factor to balance quality and file size.

Step 3: Generate Multi-Bitrate Outputs for HLS/VOD

A standard VOD architecture requires multiple renditions (e.g., 1080p, 720p, 480p) packaged as HTTP Live Streaming (HLS) or MPEG-DASH.

The following command generates an HLS playlist with a scaled 720p stream inside the container:

docker run --rm -v $(pwd):/workspace -w /workspace jrottenberg/ffmpeg:latest \
  -i input.mp4 \
  -filter_complex "[0:v]scale=w=1280:h=720[v720]" \
  -map "[v720]" -c:v:0 libx264 -b:v:0 2800k \
  -map 0:a -c:a aac -b:a 128k \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type vod \
  -hls_segment_filename "segment_720p_%03d.ts" \
  playlist_720p.m3u8

Step 4: Scale the Pipeline on Demand

To handle unpredictable processing loads, integrate the Dockerized FFmpeg workflow into an automated architecture:

  1. Job Queue: Push transcoding jobs (metadata, source file URI, target resolutions) to a message queue such as RabbitMQ, Redis, or AWS SQS.
  2. Worker Pool: Create background worker services (written in Python, Node.js, or Go) that listen to the queue.
  3. Container Invocation: When a worker receives a job, it invokes the FFmpeg Docker container via the Docker Engine API or a system call, pulling the raw file from cloud storage (e.g., Amazon S3, Google Cloud Storage), processing the renditions, and pushing the output back.
  4. Autoscaling: Use Kubernetes with KEDA (Kubernetes Event-driven Autoscaling) to automatically increase the number of worker pods when the message queue grows, and scale down to zero when idle.

Step 5: Enable Hardware Acceleration (Optional)

Software encoding with libx264 is CPU-intensive. If your host machine has an NVIDIA GPU, pass the GPU directly to the container to speed up scaling and encoding tasks:

docker run --rm --gpus all -v $(pwd):/workspace -w /workspace jrottenberg/ffmpeg:latest \
  -hwaccel cuda \
  -i input.mp4 \
  -vf "scale_cuda=1280:720" \
  -c:v h264_nvenc -preset p4 \
  output_720p.mp4

This reduces CPU utilization and significantly shortens processing time for on-demand pipelines.