# ffmpeg-static: building one self-contained ffmpeg binary with every codec

> zimbatm's ffmpeg-static is a set of shell scripts that compile ffmpeg and its dependencies into a static binary, including the h264 and webm codecs that the usual distribution packages leave out. It is community-supported, and the README is refreshingly honest about the parts it does not fully solve.

**zimbatm/ffmpeg-static** — Scripts to build ffmpeg with all the deps statically (webm + h264 included)

- Repository: https://github.com/zimbatm/ffmpeg-static
- Stars: 540 · Forks: 257
- Language: Shell
- License: ISC
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/zimbatm-ffmpeg-static

## One binary, and the codecs distributions leave out

This repository holds scripts to build ffmpeg with all the dependencies statically, with webm and h264 included, and the status line at the top says community-supported.

The use case is narrow and specific, and it is worth naming precisely, because a general ffmpeg build system is not what this is. Most Linux distributions ship an ffmpeg without the h264 encoder, because h264 is patent encumbered and the distributions avoid shipping it as a codec. A static build sidesteps that by compiling the encoder in yourself, so the resulting binary is self contained and carries the codec with it.

There are three scripts, and the workflow is simple. You install the build dependencies, run ./build.sh, wait, and you get the ffmpeg binary in target/bin.

The dependency list for Debian and Ubuntu is:

    # Debian & Ubuntu
    $ apt-get install build-essential curl tar libass-dev libtheora-dev libvorbis-dev libtool cmake automake autoconf

And on OS X the steps are manual: install XCode, install the XCode command line tools, install homebrew, then brew install openssl frei0r sdl2 wget.

The project is ISC licensed, the primary language is Shell, the default branch is master, and the last push was on 2026-04-24. The repository publishes no GitHub releases, which fits a set of scripts rather than a distributed artefact, though there is a Dockerfile for building the binaries in a container.

## The three flags, and the one that trips people up

The build command takes three flags, and their interaction matters more than their number.

    $ ./build.sh [-j <jobs>] [-B] [-d]
    # ... wait ...
    # binaries can be found in ./target/bin/

The -j flag sets the number of parallel jobs, which is the difference between a build that takes a coffee break and one that takes the afternoon.

The -B flag reconfigures and rebuilds all the packages, and the README explains why you need it. If you have built ffmpeg before with build.sh, the default behaviour is to keep the previous configuration. So the default is a cache, not a fresh build, and the first time you change a configuration option and wonder why nothing happened, this is the reason.

The -d flag only downloads and unpacks the dependencies without building them, which is the flag to reach for when you want to see what the build is going to do before spending the time.

For Ubuntu users there is a one command route, which downloads the dependencies and installs in one command:

    $ sudo ./build-ubuntu.sh

And one detail is easy to miss. If you are going to use the h264 presets, make sure to copy them along with the binaries, and the README shows where to put them:

    $ mkdir ~/.ffmpeg
    $ cp ./target/share/ffmpeg/*.ffpreset ~/.ffmpeg

The presets are the x264 speed and quality profiles. They are configuration files rather than compiled-in defaults, so a static binary without them still works but falls back to ffmpeg's built-in defaults, which is a quieter difference than you would expect between machines.

## Building in a container, and getting the binary out

The Docker route is there for two reasons, reproducibility and isolation, and the README shows the whole cycle:

    $ docker build -t ffmpeg-static .
    $ docker run -it ffmpeg-static
    $ ./build.sh [-j <jobs>] [-B] [-d]

The binaries are created in the /ffmpeg-static/bin directory, and the method of getting them out of the container is up to you. The /ffmpeg-static path is a Docker volume, which is the mechanism that makes extraction straightforward: a named or mounted volume means the files exist on the host after the container exits.

The Dockerfile itself shows what the build needs. It is based on ubuntu:noble, installs a small set of packages for downloading and unpacking dependencies, bzip2, perl, tar, wget and xz-utils, and then a much longer list of compilation packages, among them the codec development headers for fontconfig, freetype, opencore, SDL2, speex, theora, va, vdpau, vo-amrwbenc, vorbis, webp, xcb, xvidcore and frei0r plugins, plus yasm for the assembly, texi2html for the documentation, and autoconf, automake, cmake, libtool and pkg-config for the build system itself.

Two things are worth noting about that list. The presence of yasm is not incidental, since several codec projects need an assembler, and the TODO list in the README mentions that OSX's xvidcore does not detect yasm correctly, which is a known gap rather than a configuration mistake on your side.

The base image being a current Ubuntu release is also why the resulting binary is not portable backwards, which is the subject of the remaining links section.

## Static does not mean the same everywhere

There is a short section on remaining links, and it is the most useful thing in the README for anyone about to ship one of these binaries in a container image.

The author is direct about the uncertainty: I'm not sure it's a good idea to statically link those, but it probably means the executable won't work across distributions or even across releases.

The demonstration is on two platforms. On Ubuntu 10.04, ldd on the built ffmpeg reports not a dynamic executable. On OS X 10.6.4, otool -L on the binary shows a dependency on the system library, /usr/lib/libSystem.B.dylib, with a compatibility version of 1.0.0 and a current version of 125.2.0.

So the Linux binary is genuinely static, and the macOS one is not, because the system libraries cannot be linked statically on that platform. The consequence is that a static Linux build will run on an old glibc, which is the usual reason to want one, while the macOS build is bound to the system it was built against.

That is also the answer to why you would build this rather than use a distribution package. A static binary does not care about the target's library versions, which matters when you are shipping to a fleet of machines you do not control, or building a container image with a minimal base. The price is a long build and a binary that will not be patched for upstream fixes unless you rebuild it.

## Community maintenance, and the tests that are not there yet

The community section explains how the project is run, and it is a good description of a small open source project with a clear deal.

This repository is community-supported. If you make a useful PR then you will be added to the repo as a contributor. All changes are assumed to be licensed under the same license as the project, which is ISC. As a contributor you can do whatever you want, and the specific asks are to help maintain the scripts, upgrade dependencies and merge other people's pull requests. The one rule is to be responsible and make an issue if you want to introduce bigger changes, so they can be discussed beforehand.

The TODO list is short and honest about the gaps. One item is to add some tests to check that video output is correctly generated, which the author notes would help upgrading the package without too much work. Another is that OSX's xvidcore does not detect yasm correctly, and the third is to remove remaining libs, with a question mark.

That first TODO is the one to keep in mind as a user. There is no test suite, which means nothing verifies that a rebuilt binary still produces correct video. The scripts are maintained by whoever shows up, and the dependency versions are tracked with renovate.json for automated updates, which is a sensible way to keep a build script current without a maintainer doing it by hand.

The repository layout is consistent with that: build.sh, build-ubuntu.sh, download.pl for fetching the dependency tarballs, env.source for the debug workflow, fetchurl for the download logic, the Dockerfile, a .travis.yml for CI, and renovate.json for the dependency updates.

## Conclusion

Adopt ffmpeg-static if you need to ship one ffmpeg binary to a machine whose package manager will not give you h264, or if you need a build that does not depend on the distribution's library versions. Do not adopt it if you can install ffmpeg from your distribution, because a from-source build with every dependency is a long process to repeat for a package you could have had in seconds. Before you start, read the flags, since the default keeps your previous configuration and you need -B to reconfigure and rebuild everything, and note the h264 presets are not copied into the binary, so you have to copy them yourself.

## FAQ

### How do I build a static ffmpeg with zimbatm's scripts?

Install the build dependencies, for Debian and Ubuntu `apt-get install build-essential curl tar libass-dev libtheora-dev libvorbis-dev libtool cmake automake autoconf`, then run `./build.sh [-j <jobs>] [-B] [-d]` and wait. The binary ends up in ./target/bin/. Ubuntu users can do it in one command with `sudo ./build-ubuntu.sh`.

### What do the -B and -d flags do in build.sh?

The default behaviour keeps your previous configuration, so -B reconfigures and rebuilds all the packages, and -d only downloads and unpacks the dependencies without building them. The -j flag sets the number of parallel jobs.

### Do I need to copy anything alongside the static ffmpeg binary?

The h264 presets. The README says to make sure you copy them along with the binaries, with `mkdir ~/.ffmpeg` and `cp ./target/share/ffmpeg/*.ffpreset ~/.ffmpeg`, since the presets are configuration files rather than compiled-in defaults.

### Is the zimbatm ffmpeg-static build portable across Linux distributions?

The Linux build is genuinely static, with ldd reporting not a dynamic executable, but the author warns that statically linking the remaining libraries probably means the executable will not work across distributions or even across releases. The macOS build is not static at all, since otool shows a dependency on the system library.

## Sources

- [Issues](https://github.com/zimbatm/ffmpeg-static/issues)
- [License: ISC](https://github.com/zimbatm/ffmpeg-static/blob/master/LICENSE)
- [README](https://github.com/zimbatm/ffmpeg-static/blob/master/README.md)
- [zimbatm/ffmpeg-static on GitHub](https://github.com/zimbatm/ffmpeg-static)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zimbatm-ffmpeg-static
