An AWS Lambda Layer that bundles a static FFmpeg & ffprobe
release, ready to use on the Amazon Linux 2023 Lambda runtimes
(nodejs20.x, nodejs22.x, python3.12, python3.13,
provided.al2023, and friends).
The binaries come from johnvansickle.com's
"release" tarball — the de-facto static FFmpeg distribution. The script
fetches the unversioned ffmpeg-release-{arch}-static.tar.xz URL, so it
automatically rolls forward as upstream publishes new versions
(currently FFmpeg 7.0.2, will move to 7.1.x → 8.x as the mirror
catches up). They are built against glibc 2.17, so they run unmodified
on AL2023's glibc 2.34.
Once the layer is attached to a function, the binaries are available at:
/opt/bin/ffmpeg
/opt/bin/ffprobe
/opt/bin is already on $PATH in every Lambda runtime, so your handler
can just call ffmpeg ... / ffprobe ... directly.
The only public 8.1 static distribution today is
BtbN/FFmpeg-Builds. Its
linux64-gpl-8.1 binary is ~203 MB per executable; ffmpeg + ffprobe
totals ~387 MB unzipped — far over Lambda's 250 MB function + layers
unzipped quota. The LGPL variant is no smaller (~352 MB combined). For
a usable 8.1 layer we would need to either:
- Build FFmpeg 8.1 from source in an AL2023 container with
--disable-everything+ a minimal feature set (~50–80 MB), - or UPX-compress BtbN's binary (cuts size ~50% at the cost of cold start latency).
Both are bigger projects than the current scope. Switching to BtbN once they ship a slimmer build, or doing our own source build, is on the roadmap. Until then this layer ships the johnvansickle 7.x release line.
.
├── build.sh # downloads + stages ffmpeg into build/layer/bin
├── template.yaml # SAM template + AWS::ServerlessRepo metadata
├── Makefile # build / deploy / publish, modeled on the
│ # serverlesspub reference example
├── LICENSE # GPL-3.0 (matches the bundled FFmpeg)
├── NOTICE # attribution & licensing notes
└── README.md
./build/layer/ is generated at build time and contains:
build/layer/
└── bin/
├── ffmpeg
└── ffprobe
Once the app is published in the Serverless Application Repository (SAR),
anyone can deploy it into their account with a single sam deploy or via
the AWS console. The stack output LayerVersionArn is what you attach to
your Lambda functions.
Example consumer SAM template:
Transform: AWS::Serverless-2016-10-31
Resources:
Ffmpeg:
Type: AWS::Serverless::Application
Properties:
Location:
ApplicationId: arn:aws:serverlessrepo:us-east-1:<YOUR_ACCOUNT_ID>:applications/ffmpeg-lambda-layer-for-amazon-linux-2023
SemanticVersion: 1.0.0
Transcoder:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs20.x
Handler: index.handler
CodeUri: src/
MemorySize: 1024
Timeout: 60
Layers:
- !GetAtt Ffmpeg.Outputs.LayerVersionArnMinimal Node.js handler:
import { spawnSync } from "node:child_process";
export const handler = async () => {
const out = spawnSync("ffmpeg", ["-version"], { encoding: "utf8" });
return { stdout: out.stdout, stderr: out.stderr, code: out.status };
};Prerequisites:
- macOS or Linux with
bash,curl,tar,xz - AWS CLI (used for
aws cloudformation package/deploy) - AWS SAM CLI (used only for
make publish) - AWS credentials configured (
aws configureor SSO)
Override any of these on the command line, e.g.
make deploy DEPLOYMENT_BUCKET=my-bucket ARCH=arm64.
| Variable | Required for | Default | What it does |
|---|---|---|---|
DEPLOYMENT_BUCKET |
deploy, publish |
(none, must be set) | S3 bucket used by aws cloudformation package and sam publish to upload the layer zip. SAR also reads from this bucket — see "S3 bucket policy" below. |
STACK_NAME |
deploy |
ffmpeg-lambda-layer-for-amazon-linux-2023 |
CloudFormation stack name created/updated by make deploy. |
REGION |
all targets | us-east-1 |
AWS region for every CLI call. us-east-1 is the canonical region for SAR. |
ARCH |
build (and anything that depends on it) |
amd64 |
Picks which static tarball to download. amd64 → x86_64 Lambda; arm64 → Graviton. Remember to flip CompatibleArchitectures in template.yaml if you publish an arm64 layer. |
The Makefile uses make's natural dependency tracking, just like the
reference serverlesspub/ffmpeg-aws-lambda-layer:
build/layer/bin/ffmpeg— recipe runs./build.sh; skipped if the file already exists. Usemake cleanto force a re-download.build/output.yaml— recipe runsaws cloudformation package; re-runs only iftemplate.yamlor the staged binary changed.deployandpublishdepend onbuild/output.yaml, so they transparently trigger packaging when needed.
make build # 1. stage ffmpeg/ffprobe (downloads ~25 MB tarball)
make deploy DEPLOYMENT_BUCKET=my-bucket # 2. (optional) test the layer in your own accountmake build produces ./build/layer/bin/ffmpeg and
./build/layer/bin/ffprobe, verified against the upstream MD5 checksum.
To clean up a test deployment:
aws cloudformation delete-stack --stack-name ffmpeg-lambda-layer-for-amazon-linux-2023Note: the layer has
RetentionPolicy: Retain, so deleting the stack does not delete the layer version. Delete it manually if needed:aws lambda delete-layer-version --layer-name ffmpeg --version-number N.
End-state goal: your application appears at
serverlessrepo.aws.amazon.com/applications
under the name ffmpeg-lambda-layer-for-amazon-linux-2023.
1.1. Pick a region. SAR is a regional service; us-east-1 is the
most common publishing region. Set it once for the session:
export AWS_REGION=us-east-11.2. Create an S3 bucket that will hold packaged layer zips:
aws s3 mb s3://my-sar-artifacts-<your-suffix> --region us-east-11.3. Allow SAR to read from that bucket. Save the policy below as
bucket-policy.json (replace REPLACE_* first), then apply it:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowServerlessRepoReadAccess",
"Effect": "Allow",
"Principal": { "Service": "serverlessrepo.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::REPLACE_WITH_YOUR_BUCKET_NAME/*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "REPLACE_WITH_YOUR_AWS_ACCOUNT_ID"
}
}
}
]
}aws s3api put-bucket-policy \
--bucket my-sar-artifacts-<your-suffix> \
--policy file://bucket-policy.jsonWithout this policy the publish step will fail with Access Denied.
1.4. Fill in your metadata in template.yaml under
Metadata.AWS::ServerlessRepo::Application:
Author— your name or orgHomePageUrl,SourceCodeUrl— currentlyhttps://github.com/cookiemonsterdev/ffmpeg-aws-lambda-layerSemanticVersion— bump for every release (1.0.0,1.0.1, …)
make build
make publish DEPLOYMENT_BUCKET=my-sar-artifacts-<your-suffix>make publish internally runs aws cloudformation package to upload
the layer zip and rewrite the template to build/output.yaml, then
calls sam publish --template build/output.yaml --region us-east-1.
On success you'll see output similar to:
Publish Succeeded
Created new application with the following metadata:
{
"Name":"ffmpeg-lambda-layer-for-amazon-linux-2023",
"Description":"Latest static FFmpeg & ffprobe ...",
"Author":"Your Name",
...
"SemanticVersion":"1.0.0",
"SourceCodeUrl":"https://github.com/cookiemonsterdev/ffmpeg-aws-lambda-layer"
}
Click the link below to view your application in AWS console:
https://console.aws.amazon.com/serverlessrepo/home?region=us-east-1#/published-applications/arn:aws:serverlessrepo:us-east-1:<ACCOUNT_ID>:applications~ffmpeg-lambda-layer-for-amazon-linux-2023
At this point the app is private — only your account can see it.
You can share the app with specific accounts, or make it public so it shows up on the public SAR listing.
APP_ARN="arn:aws:serverlessrepo:us-east-1:<ACCOUNT_ID>:applications/ffmpeg-lambda-layer-for-amazon-linux-2023"
# Public (anyone can deploy)
aws serverlessrepo put-application-policy \
--application-id "$APP_ARN" \
--statements Principals=*,Actions=Deploy \
--region us-east-1
# Shared with specific accounts
aws serverlessrepo put-application-policy \
--application-id "$APP_ARN" \
--statements Principals=111122223333,444455556666,Actions=Deploy \
--region us-east-1AWS reviews newly-public applications before they appear on the public SAR website. The app is immediately deployable via
arn:aws:serverlessrepo:...once the policy is applied, but the browsable public listing can take a few hours to a day to show up.
-
Bump
Metadata.AWS::ServerlessRepo::Application.SemanticVersionintemplate.yaml(1.0.0→1.0.1, etc.). -
Re-run the build + publish chain:
make clean make publish DEPLOYMENT_BUCKET=my-sar-artifacts-<your-suffix>
sam publish will detect the existing application and add a new version
instead of creating a new one.
-
Access Deniedonsam publish— the S3 bucket is missing theserverlessrepo.amazonaws.coms3:GetObjectpolicy from Step 1.3. -
InvalidParameterValueException: Cannot find a license— make sureLicenseUrl: LICENSEpoints to a real file in the repo root; the current template already does. -
Layer too large — Lambda layers are limited to 250 MB unzipped (combined with function code and other layers) / ~70 MB zipped. The full GPL FFmpeg 7.x static build is ~152 MB unzipped, ~50 MB zipped — comfortably within limits. If you only need
ffmpegyou can editbuild.shto skip copyingffprobe. -
ARM64 functions — re-run
make build ARCH=arm64, changeCompatibleArchitecturesintemplate.yamltoarm64, bump the semver, and publish as a separate version (or a separate app, e.g.ffmpeg-lambda-layer-for-amazon-linux-2023-arm64). -
Verify the binary on Lambda:
aws lambda invoke --function-name your-fn --payload '{}' /tmp/out.json cat /tmp/out.json # should show ffmpeg version banner
The bundled FFmpeg build includes GPL components (x264, x265, …), so the
layer as a whole is distributed under GPL-3.0-or-later. See
LICENSE and NOTICE for the full terms and
attribution. If you need an LGPL-only build, see the note in NOTICE.