Optimize ffmpeg and libvips Dockerfile builds (#37401)

Signed-off-by: Michael Stanclift <mx@vmstan.com>
This commit is contained in:
Michael Stanclift 2026-04-09 04:46:57 -05:00 committed by GitHub
parent 519b00f25b
commit 79505180a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -25,8 +25,8 @@ FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
# Example: v4.3.0-nightly.2023.11.09+pr-123456 # Example: v4.3.0-nightly.2023-11-09+pr-123456
# Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"] # Overwrite existence of 'alpha.X' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023-11-09"]
ARG MASTODON_VERSION_PRERELEASE="" ARG MASTODON_VERSION_PRERELEASE=""
# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"] # Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-123456"]
ARG MASTODON_VERSION_METADATA="" ARG MASTODON_VERSION_METADATA=""
@ -48,29 +48,27 @@ ARG GID="991"
# Apply Mastodon build options based on options above # Apply Mastodon build options based on options above
ENV \ ENV \
# Apply Mastodon version information
MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \ MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
SOURCE_COMMIT="${SOURCE_COMMIT}" \ SOURCE_COMMIT="${SOURCE_COMMIT}" \
# Apply Mastodon static files and YJIT options RAILS_SERVE_STATIC_FILES="${RAILS_SERVE_STATIC_FILES}" \
RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \ RUBY_YJIT_ENABLE="${RUBY_YJIT_ENABLE}" \
RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \ TZ="${TZ}"
# Apply timezone
TZ=${TZ}
# Configure runtime environment
# BIND: IP to bind Mastodon to when serving traffic
# NODE_ENV/RAILS_ENV: production settings for Node.js and Ruby on Rails
# DEBIAN_FRONTEND: suppress interactive prompts
# PATH: add Ruby and Mastodon installation directories
# MALLOC_CONF: optimize jemalloc 5.x performance
# MASTODON_SIDEKIQ_READY_FILENAME: Sidekiq readiness check filename for Kubernetes
ENV \ ENV \
# Configure the IP to bind Mastodon to when serving traffic
BIND="0.0.0.0" \ BIND="0.0.0.0" \
# Use production settings for Yarn, Node.js and related tools
NODE_ENV="production" \ NODE_ENV="production" \
# Use production settings for Ruby on Rails
RAILS_ENV="production" \ RAILS_ENV="production" \
# Add Ruby and Mastodon installation to the PATH
DEBIAN_FRONTEND="noninteractive" \ DEBIAN_FRONTEND="noninteractive" \
PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \ PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
# Optimize jemalloc 5.x performance
MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \ MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
# Sidekiq will touch tmp/sidekiq_process_has_started_and_will_begin_processing_jobs to indicate it is ready. This can be used for a readiness check in Kubernetes
MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs MASTODON_SIDEKIQ_READY_FILENAME=sidekiq_process_has_started_and_will_begin_processing_jobs
# Set default shell used for running commands # Set default shell used for running commands
@ -99,10 +97,10 @@ RUN \
# Mount Apt cache and lib directories from Docker buildx caches # Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ --mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Apt update & upgrade to check for security updates to Debian image # Update package list and upgrade system packages
apt-get update; \ apt-get update; \
apt-get dist-upgrade -yq; \ apt-get dist-upgrade -yq; \
# Install jemalloc, curl and other necessary components # Install jemalloc and other necessary components
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
curl \ curl \
file \ file \
@ -112,6 +110,42 @@ RUN \
tini \ tini \
tzdata \ tzdata \
wget \ wget \
# Mastodon components
libexpat1 \
libglib2.0-0t64 \
libicu76 \
libidn12 \
libpq5 \
libreadline8t64 \
libssl3t64 \
libyaml-0-2 \
# libvips components
libcgif0 \
libexif12 \
libheif1 \
libhwy1t64 \
libimagequant0 \
libjpeg62-turbo \
liblcms2-2 \
libspng0 \
libtiff6 \
libwebp7 \
libwebpdemux2 \
libwebpmux3 \
# ffmpeg components
libdav1d7 \
libmp3lame0 \
libopencore-amrnb0 \
libopencore-amrwb0 \
libopus0 \
libsnappy1v5 \
libtheora0 \
libvorbis0a \
libvorbisenc2 \
libvorbisfile3 \
libvpx9 \
libx264-164 \
libx265-215 \
; \ ; \
# Patch Ruby to use jemalloc # Patch Ruby to use jemalloc
patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \ patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \
@ -120,42 +154,37 @@ RUN \
patchelf \ patchelf \
; ;
# Create temporary build layer from base image # Build stage for media libraries (libvips, ffmpeg)
FROM ruby AS build FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS media-build
ARG TARGETPLATFORM ARG TARGETPLATFORM
# Set default shell used for running commands
SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
# hadolint ignore=DL3008 # hadolint ignore=DL3008
RUN \ RUN \
# Mount Apt cache and lib directories from Docker buildx caches --mount=type=cache,id=apt-native-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=apt-native-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ # Remove automatic apt cache Docker cleanup scripts
# Install build tools and bundler dependencies from APT rm -f /etc/apt/apt.conf.d/docker-clean; \
# Install build tools for native libraries
apt-get update; \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
autoconf \ autoconf \
automake \ automake \
build-essential \ build-essential \
cmake \
git \
libgdbm-dev \
libglib2.0-dev \
libgmp-dev \
libicu-dev \
libidn-dev \
libpq-dev \
libssl-dev \
libtool \ libtool \
libyaml-dev \
meson \ meson \
nasm \ nasm \
pkg-config \ pkg-config \
shared-mime-info \
xz-utils \ xz-utils \
# libvips components # libvips components
libcgif-dev \ libcgif-dev \
libexif-dev \ libexif-dev \
libexpat1-dev \ libexpat1-dev \
libgirepository1.0-dev \ libgirepository1.0-dev \
libglib2.0-dev \
libheif-dev \ libheif-dev \
libhwy-dev \ libhwy-dev \
libimagequant-dev \ libimagequant-dev \
@ -176,8 +205,8 @@ RUN \
libx265-dev \ libx265-dev \
; ;
# Create temporary libvips specific build layer from build layer # Create temporary libvips specific build layer
FROM build AS libvips FROM media-build AS libvips
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"] # libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips # renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
@ -192,15 +221,16 @@ RUN tar xf vips-${VIPS_VERSION}.tar.xz;
WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION} WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION}
# Configure and compile libvips # Configure libvips
RUN \ RUN meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false
meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false; \
cd build; \
ninja; \
ninja install;
# Create temporary ffmpeg specific build layer from build layer WORKDIR /usr/local/libvips/src/vips-${VIPS_VERSION}/build
FROM build AS ffmpeg
# Compile and install libvips
RUN ninja && ninja install
# Create temporary ffmpeg specific build layer
FROM media-build AS ffmpeg
# ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"] # ffmpeg version to compile, change with [--build-arg FFMPEG_VERSION="7.0.x"]
# renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg # renovate: datasource=repology depName=ffmpeg packageName=openpkg_current/ffmpeg
@ -241,17 +271,48 @@ RUN \
--enable-shared \ --enable-shared \
--enable-version3 \ --enable-version3 \
; \ ; \
make -j$(nproc); \ make -j"$(nproc)"; \
make install; make install;
# Create temporary build layer from base image for Ruby dependencies
FROM ruby AS ruby-build
ARG TARGETPLATFORM
# hadolint ignore=DL3008
RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Install build tools and bundler dependencies from APT
apt-get install -y --no-install-recommends \
build-essential \
git \
libgdbm-dev \
libgmp-dev \
libicu-dev \
libidn-dev \
libpq-dev \
libssl-dev \
libyaml-dev \
shared-mime-info \
zlib1g-dev \
;
# Create temporary bundler specific build layer from build layer # Create temporary bundler specific build layer from build layer
FROM build AS bundler FROM ruby-build AS bundler
ARG TARGETPLATFORM ARG TARGETPLATFORM
# Copy Gemfile config into working directory # Copy Gemfile config into working directory
COPY Gemfile* /opt/mastodon/ COPY Gemfile* /opt/mastodon/
# Copy libvips for gems that need it during install
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
COPY --from=libvips /usr/local/libvips/include /usr/local/include
RUN ldconfig
RUN \ RUN \
# Mount Ruby Gem caches # Mount Ruby Gem caches
--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \ --mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \
@ -267,7 +328,7 @@ RUN \
bundle install -j"$(nproc)"; bundle install -j"$(nproc)";
# Create temporary assets build layer from build layer # Create temporary assets build layer from build layer
FROM build AS precompiler FROM ruby-build AS precompiler
ARG TARGETPLATFORM ARG TARGETPLATFORM
@ -311,53 +372,6 @@ FROM ruby AS mastodon
ARG TARGETPLATFORM ARG TARGETPLATFORM
# hadolint ignore=DL3008
RUN \
# Mount Apt cache and lib directories from Docker buildx caches
--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
# Mount Corepack and Yarn caches from Docker buildx caches
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
# Apt update install non-dev versions of necessary components
apt-get install -y --no-install-recommends \
libexpat1 \
libglib2.0-0t64 \
libicu76 \
libidn12 \
libpq5 \
libreadline8t64 \
libssl3t64 \
libyaml-0-2 \
# libvips components
libcgif0 \
libexif12 \
libheif1 \
libhwy1t64 \
libimagequant0 \
libjpeg62-turbo \
liblcms2-2 \
libspng0 \
libtiff6 \
libwebp7 \
libwebpdemux2 \
libwebpmux3 \
# ffmpeg components
libdav1d7 \
libmp3lame0 \
libopencore-amrnb0 \
libopencore-amrwb0 \
libopus0 \
libsnappy1v5 \
libtheora0 \
libvorbis0a \
libvorbisenc2 \
libvorbisfile3 \
libvpx9 \
libx264-164 \
libx265-215 \
;
# Copy Mastodon sources into final layer # Copy Mastodon sources into final layer
COPY . /opt/mastodon/ COPY . /opt/mastodon/