← All posts

Lambda で FFmpeg が水に合わない理由——Serverless で踏んだ落とし穴

AWS Lambda、Vercel、Cloudflare Workers で FFmpeg を動かそうとして詰まった点——バイナリサイズ、タイムアウト、メモリ、コールドスタートと現実的な代替案。

FFHub·2026-05-02
Lambda で FFmpeg が水に合わない理由——Serverless で踏んだ落とし穴

Serverless は「無限にスケール、運用ゼロ」を謳います。だから動画処理——クリップのトリム、サムネ生成、アップロード変換——が必要になったとき、最初に思い付くのは「Lambda で FFmpeg を動かせばいいじゃん」という発想です。どれくらい大変なんだ、と。

実際にやると、めちゃくちゃ大変でした。この記事では、Serverless で FFmpeg を動かすときに実際に遭遇する問題、よくある回避策とその限界、そしてどこから先は専用 API を使ったほうが良いのかをまとめます。

なぜ Serverless で FFmpeg はつらいのか

1. バイナリサイズ——スタート時点で 70MB 超

主要コーデック(H.264、H.265、VP9、AAC、Opus)入りで静的ビルドした FFmpeg は 70〜100 MB。libass(字幕)や libfdk-aac(高音質 AAC)まで足すと 120 MB 超。

プラットフォーム側の上限がきつい:

プラットフォームデプロイサイズ上限内訳
AWS Lambda(zip)50 MB(unzip 後 250 MB)コード + 依存 + バイナリ
AWS Lambda(コンテナ)10 GB余裕あるが cold start が遅い
Vercel Serverless Functions50 MB圧縮後
Cloudflare Workers10 MB(有料プラン)ネイティブバイナリ非対応
Google Cloud Functions500 MB(ソース)比較的緩い

AWS Lambda の zip デプロイだと FFmpeg バイナリだけで 50 MB 枠の大半を持っていかれる。アプリ本体のコードが残った隙間で取り合うことになる。

2. 実行時間タイムアウト——最大 15 分

AWS Lambda の最大実行時間は 15 分。他はもっと厳しい:

プラットフォーム最大タイムアウト
AWS Lambda15 分
Google Cloud Functions9 分(2nd gen で 60 分)
Vercel Serverless5 分(Pro)、60s(Hobby)
Cloudflare Workers30s(標準)、15 分(Workflows)

15 分は十分長そうに見える。FFmpeg の現実的な処理時間を見るまでは:

タスク1080p、10 分動画2 vCPU での所要時間
サムネイル抽出1 フレーム< 1 秒
音声抽出ストリームコピー< 5 秒
H.264 トランスコードCRF 23、medium8〜12 分
H.265 トランスコードCRF 28、medium15〜25 分
字幕焼き込み再エンコード10〜15 分
VP9 トランスコードCRF 3020〜40 分

サムネと音声抽出は楽勝。でも数分以上の動画の本格的なトランスコードは、Lambda タイムアウトに引っかかるか、危険なほど近づくかのどちらかです。エンコード時間に効く設定は 動画圧縮のベストプラクティス を参照。

3. メモリ制限——大きいファイルが置けない

動画処理はメモリを食う。FFmpeg は入出力をバッファし、複雑なフィルタは複数フレームを同時に保持します。

プラットフォーム最大メモリ
AWS Lambda10 GB
Google Cloud Functions32 GB(2nd gen)
Vercel Serverless3 GB
Cloudflare Workers128 MB

Lambda の 10 GB は良さそうに見える——ただしメモリは直接コストに効きます。Lambda は GB 秒課金。4 GB を 10 分使う関数は、256 MB を 15 秒使う関数より 40 倍 高い。

実測のメモリ使用量:

タスク典型的な使用メモリ
サムネイル抽出100〜200 MB
音声抽出50〜150 MB
1080p H.264 トランスコード500 MB〜1.5 GB
4K H.264 トランスコード2〜4 GB
複雑なフィルタチェーン1〜4 GB

4. GPU が使えない

ハードウェアエンコード(NVENC、QSV、VAML)は H.264/H.265 で 5〜10 倍速い。が、Serverless は GPU を提供しません。

つまり CPU エンコード一択。これは:

  • GPU エンコードより 5〜10 倍遅い
  • 大規模では動画 1 本あたりのコストが高い
  • タイムアウト上限に引っかかりやすい

5. コールドスタートのペナルティ

cold start のたびに FFmpeg バイナリを実行環境にロードする必要があります。70 MB 超のバイナリで、プラットフォーム側の初期化に加えて 1〜3 秒 のコールドスタート遅延が乗ります。

コンテナベース Lambda(大きいバイナリを載せるなら必須)だと cold start が 5〜15 秒 にまで伸びることもある。

6. 一時ストレージの制約

Lambda は /tmp をデフォルト 512 MB(追加課金で 10 GB まで)提供。動画ファイルはでかい——10 分の 1080p 動画は 500 MB〜1 GB。入力と出力の両方を置く場所が要ります:

入力ファイル:  500 MB
出力ファイル:  300 MB
FFmpeg 一時:  200 MB
─────────────────
合計:         1 GB  ← デフォルト /tmp を超える

7. 永続プロセスがない

FFmpeg はプロセスを温めて使い回せると効率的(バイナリのウォーム保持、デコードキャッシュ、コネクション再利用)。Serverless はステートレス・短命。呼び出しごとに毎回ゼロから。

よくある回避策

それでも Serverless で FFmpeg を動かすチームはあります。代表的なアプローチ:

Lambda Layers

AWS Lambda Layers を使うと FFmpeg をアプリコードと別パッケージにできる:

# Download a static FFmpeg build
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar xf ffmpeg-release-amd64-static.tar.xz

# Create Lambda layer structure
mkdir -p ffmpeg-layer/bin
cp ffmpeg-*-amd64-static/ffmpeg ffmpeg-layer/bin/
cd ffmpeg-layer && zip -r ../ffmpeg-layer.zip .

Lambda 関数側:

const { execSync } = require('child_process');

exports.handler = async (event) => {
  // FFmpeg binary from layer is at /opt/bin/ffmpeg
  const result = execSync(
    '/opt/bin/ffmpeg -i /tmp/input.mp4 -vf "scale=-2:720" -crf 23 /tmp/output.mp4',
    { timeout: 300000 }
  );
  return { statusCode: 200 };
};

限界:

  • Layer サイズも 250 MB unzip 上限にカウントされる
  • 必要なコーデックだけに FFmpeg を絞り込む必要あり
  • 自前 FFmpeg ビルドのメンテが恒常的な負担になる

Docker Container Images

Lambda は最大 10 GB のコンテナイメージを許容するので、サイズ的な余裕は出ます:

FROM public.ecr.aws/lambda/nodejs:20

# Install FFmpeg
RUN yum install -y tar xz && \
    curl -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz | \
    tar xJ --strip-components=1 -C /usr/local/bin/ --wildcards '*/ffmpeg' '*/ffprobe'

COPY index.mjs ${LAMBDA_TASK_ROOT}/
CMD ["index.handler"]

限界:

  • コールドスタートが目に見えて長くなる(5〜15 秒)
  • イメージが大きいぶん ECR ストレージコストが上がる
  • 15 分タイムアウトと GPU 非対応は相変わらず

ffmpeg.wasm——ブラウザ/Edge 用 FFmpeg

ffmpeg.wasm は FFmpeg を WebAssembly にコンパイルしたもの。ブラウザや Edge ランタイムで動く:

import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile } from '@ffmpeg/util';

const ffmpeg = new FFmpeg();
await ffmpeg.load();
await ffmpeg.writeFile('input.mp4', await fetchFile(videoUrl));
await ffmpeg.exec(['-i', 'input.mp4', '-vf', 'scale=-2:720', 'output.mp4']);
const data = await ffmpeg.readFile('output.mp4');

限界:

  • ネイティブ FFmpeg より 3〜10 倍遅い
  • コーデックサポートが限定的(H.265 不可、フィルタも制限あり)
  • ブラウザ環境ではメモリも制限される
  • 数百 MB を超えるファイルは扱えない
  • ほとんどの環境でシングルスレッド

チャンク分割処理

大きい動画を分割し、並列処理して結合する:

# Split into 2-minute segments
ffmpeg -i input.mp4 -c copy -segment_time 120 -f segment -reset_timestamps 1 chunk_%03d.mp4
// Process chunks in parallel Lambda invocations
const chunks = ['chunk_000.mp4', 'chunk_001.mp4', 'chunk_002.mp4'];
const results = await Promise.all(
  chunks.map(chunk => lambda.invoke({
    FunctionName: 'process-chunk',
    Payload: JSON.stringify({ chunk })
  }).promise())
);

// Merge results
// (requires another Lambda invocation)

限界:

  • オーケストレーションが複雑化する(Step Functions、SQS など)
  • 全操作がチャンク化に向くわけではない(字幕タイミングなど)
  • 結合処理がレイテンシを足し、境界にアーティファクトが出る場合がある
  • 結局シングルプロセスより総コストが高くなりがち

削ぎ落とした FFmpeg ビルド

必要なコーデックだけでコンパイルしてバイナリを縮める:

./configure \
  --disable-everything \
  --enable-decoder=h264,aac,mp3 \
  --enable-encoder=libx264,aac \
  --enable-muxer=mp4,mp3 \
  --enable-demuxer=mov,mp4,mp3 \
  --enable-protocol=file,pipe \
  --enable-filter=scale,overlay \
  --enable-gpl --enable-libx264

15〜25 MB まで落とせるが、機能を失う。来月 VP9 が必要になったら?再ビルド。

実測例:Lambda vs 専用サーバー

差を実感してもらうため、5 分の 1080p 動画でのベンチ:

タスクLambda(3 GB、arm64)EC2 c6g.large(2 vCPU)EC2 g5.xlarge(GPU)
サムネイル0.8s0.5s0.3s
音声抽出2s1.5s1.5s
H.264 CRF 234.5 分3.2 分25s
H.265 CRF 289 分7 分35s
VP9 CRF 3012 分9 分N/A

1000 本の H.264 トランスコード(5 分・1080p)でのコスト比較:

方式1 本あたり時間1 本あたりコスト総コスト
Lambda(3 GB)4.5 分約 $0.022約 $22
EC2 c6g.large(リザーブド)3.2 分約 $0.004約 $4
EC2 g5.xlarge(GPU)25s約 $0.008約 $8

定常ワークロードなら、Lambda はリザーブド EC2 と比べておよそ 5 倍高い。1 時間に数本以上動画が来るならコスト面で勝てません。

Serverless FFmpeg が向くケース

罠だらけと言ってきましたが、ハマるユースケースもあります:

  • サムネイル生成——速い、低メモリ、上限に余裕
  • ストリームコピーでの音声抽出——ほぼ瞬時、リソース消費も少ない
  • 30 秒未満の短尺クリップ——タイムアウトせずに変換できる
  • メタデータ抽出——ffprobe はミリ秒で終わる
  • 散発的・予測不能なトラフィック——数時間リクエストが来ないこともあるケース
# These tasks work well on Lambda
ffmpeg -i input.mp4 -ss 00:00:05 -frames:v 1 thumbnail.jpg          # < 1s
ffmpeg -i input.mp4 -vn -c:a copy audio.aac                          # < 2s
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4  # < 1s

専用 API を使うべきケース

Serverless だと厳しいのは:

  • 2〜3 分を超える動画のトランスコード——タイムアウトリスク
  • H.265 や VP9 エンコード——GPU なしでは遅すぎる
  • 複雑なフィルタチェーン——字幕、オーバーレイ、マルチ入力合成
  • 大規模で予測可能な処理——コストが釣り合わなくなる
  • フルコーデックサポート——削ぎ落としビルドだと選択肢が狭い
  • GPU アクセラレーション——Serverless にはない

このあたりは専用の動画処理 API でインフラを丸ごと外したほうが楽です。マネージド系を比較するなら FFHub と AWS MediaConvert の比較 もどうぞ。

FFHub:インフラなしの Cloud FFmpeg

FFHub は Serverless が解けない問題に的を絞った Cloud FFmpeg API です。ローカルで叩くのと同じ FFmpeg コマンドを送ると、最適化されたインフラ上で実行されます。

# Instead of wrestling with Lambda layers and timeouts:
curl -X POST https://api.ffhub.io/v1/command \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "command": "-i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac output.mp4",
    "inputs": ["https://storage.example.com/input.mp4"],
    "webhook": "https://your-app.com/callback"
  }'

Serverless FFmpeg と比べた利点:

  • バイナリ管理不要——フルコーデック、常に最新
  • タイムアウトなし——どんな長さの動画でも処理可能
  • 最適化されたインフラ——動画処理向けにチューンされた専用ハードウェア
  • 同じ FFmpeg シンタックス——新しい API を覚える必要なし
  • 従量課金——アイドルインフラのコストなし

特に、本業が動画処理ではないチームには効きます——機能開発に時間を使うべきところで FFmpeg ビルドや Lambda Layer をメンテしている時間はないはずです。

判断フレームワーク

観点Serverless専用 API
動画長30 秒未満任意
タスク種別サムネ、メタデータ、音声コピートランスコード、字幕、フィルタ
ボリューム50 本/日未満任意
コーデック要件H.264 のみ任意
GPU 要不要必要(速度面で)
チーム体制専任 DevOps ありマネージド志向

まとめ

Serverless で FFmpeg を動かすことは技術的には可能です——ただし苦行です。バイナリサイズ、タイムアウト、メモリコスト、GPU の不在。サムネ生成や音声抽出みたいな簡単なタスク以外には合いません。

本格的なトランスコードを伴うワークロードなら、Lambda の請求書だけでなくカスタム FFmpeg デプロイを構築・維持するエンジニア工数まで含めた総コストで考えてください。多くのチームにとって、専用の動画処理 API のほうが、Serverless を無理やりこの用途に押し込めるより安く・確実です。FFHub という Cloud FFmpeg ソリューションAPI でのバッチトランスコード も参照を。

関連記事

Lambda で FFmpeg が水に合わない理由——Serverless で踏んだ落とし穴 | FFHub