Run FFmpeg in AWS Lambda for Video Transcoding

Running FFmpeg on AWS Lambda allows you to perform cost-effective, scalable, and serverless video transcoding without managing dedicated servers. This article provides a straight-to-the-point guide on how to run FFmpeg within AWS Lambda, covering binary preparation, deployment via Lambda Layers, and executing transcoding commands using Python.

Step 1: Obtain a Compatible FFmpeg Binary

AWS Lambda runs on Amazon Linux. You need a static FFmpeg binary compiled for the Lambda execution environment (typically x86_64 or ARM64).

  1. Download a trusted static Linux build of FFmpeg (such as the builds by John Van Sickle).
  2. Ensure you match the architecture of your Lambda function (e.g., amd64 for x86_64 or arm64 for Graviton2).

Step 2: Package FFmpeg as an AWS Lambda Layer

Lambda Layers are the easiest way to share binaries across multiple functions.

  1. Create a directory structure locally:

    mkdir -p ffmpeg-layer/bin
  2. Copy your downloaded ffmpeg and ffprobe executables into the ffmpeg-layer/bin/ directory.

  3. Ensure the binaries have execution permissions:

    chmod +x ffmpeg-layer/bin/ffmpeg ffmpeg-layer/bin/ffprobe
  4. Compress the folder into a ZIP file:

    cd ffmpeg-layer
    zip -r ../ffmpeg-layer.zip bin/
  5. Upload ffmpeg-layer.zip to AWS Lambda as a Layer via the AWS Console or AWS CLI.

When the layer is attached to your Lambda function, the binaries will be available at /opt/bin/ffmpeg.

Step 3: Write the Lambda Function Code

AWS Lambda’s root filesystem is read-only, except for the /tmp directory. You must download the source video to /tmp, perform the transcoding, and then upload the output back to Amazon S3.

Here is an example Lambda function written in Python:

import os
import subprocess
import boto3

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    # Retrieve bucket and key from S3 trigger event
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']
    
    # Define local file paths in /tmp
    input_path = f"/tmp/{os.path.basename(key)}"
    output_filename = f"transcoded_{os.path.basename(key)}"
    output_path = f"/tmp/{output_filename}"
    
    # 1. Download file from S3
    s3_client.download_file(bucket, key, input_path)
    
    # 2. Run FFmpeg command
    # Using /opt/bin/ffmpeg added by the Lambda Layer
    ffmpeg_cmd = [
        "/opt/bin/ffmpeg",
        "-y",               # Overwrite output files without asking
        "-i", input_path,   # Input file
        "-vcodec", "h264",  # Transcode to H.264
        "-acodec", "aac",   # Transcode audio to AAC
        output_path         # Output file
    ]
    
    try:
        subprocess.run(ffmpeg_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    except subprocess.CalledProcessError as e:
        print(f"FFmpeg error: {e.stderr.decode('utf-8')}")
        raise e

    # 3. Upload transcoded file back to S3
    destination_key = f"transcoded/{output_filename}"
    s3_client.upload_file(output_path, bucket, destination_key)
    
    # Cleanup /tmp to avoid storage overflow on warm starts
    os.remove(input_path)
    os.remove(output_path)
    
    return {
        'statusCode': 200,
        'body': f"Successfully transcoded and uploaded to {destination_key}"
    }

Step 4: Configure Lambda Execution Settings

To handle video transcoding successfully, you must adjust the default AWS Lambda resource limits: