From 790c7442176a667e369de001cea032227c1cf057 Mon Sep 17 00:00:00 2001 From: Andre <200930841+Andre-Bzk@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:20:49 +0200 Subject: [PATCH] build: install ffmpeg and gosu as binaries instead of apt packages Setting the service up on a new host took 20 to 30 minutes, spent almost entirely in the Debian ffmpeg dependency chain: several hundred packages of X11 and desktop libraries that dpkg unpacks and configures file by file onto an SD card. That cost is paid again on every fresh machine and turned moving the service into a half-hour commitment rather than a routine step. Prebuilt binaries remove the package manager from the build entirely. A cold build with nothing cached now finishes in about four minutes on Pi hardware, verified end to end including audio transcoding. Pinning the dependency versions is unrelated to build speed. It prevents a rebuild months from now from silently resolving versions other than those the running container was tested with. --- Dockerfile | 32 ++++++++++++++++---------------- requirements.txt | 27 +++++++++++++++------------ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index cc32eb1..d26f31e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,35 +1,35 @@ +# syntax=docker/dockerfile:1 FROM python:3.12-slim-bookworm -# Install system dependencies (ffmpeg required by yt-dlp for audio extraction) -RUN apt-get update && apt-get install -y --no-install-recommends \ - ffmpeg \ - curl \ - gosu \ - && rm -rf /var/lib/apt/lists/* +ARG TARGETARCH -# Install yt-dlp (standalone binary, always up-to-date) -RUN curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp \ - -o /usr/local/bin/yt-dlp && \ - chmod +x /usr/local/bin/yt-dlp +# ffmpeg + ffprobe as static binaries (yt-dlp needs both for audio extraction). +# The Debian package pulls in ~400 MB of X11/desktop dependencies that dpkg then +# unpacks file by file — on an SD card that alone dominates the build time. +COPY --from=mwader/static-ffmpeg:7.1 /ffmpeg /ffprobe /usr/local/bin/ + +# gosu drops privileges in the entrypoint; the binary avoids an apt transaction +ADD --chmod=755 https://github.com/tianon/gosu/releases/download/1.17/gosu-${TARGETARCH} /usr/local/bin/gosu WORKDIR /app COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install --prefer-binary -r requirements.txt +# Placed after pip: the "latest" URL changes with every yt-dlp release and would +# otherwise invalidate the dependency layer on each build +ADD --chmod=755 https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp /usr/local/bin/yt-dlp + +COPY --chmod=755 entrypoint.sh /entrypoint.sh COPY . . -RUN mkdir -p /app/tmp /app/data - ENV APP_HOST=0.0.0.0 ENV APP_PORT=8000 ENV TMP_DIR=/app/tmp EXPOSE 8000 -COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh - ENTRYPOINT ["/entrypoint.sh"] # Single worker to stay within Pi 3's memory budget CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] diff --git a/requirements.txt b/requirements.txt index 809e62b..b140493 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,15 @@ -fastapi>=0.115 -uvicorn>=0.30 -pydantic>=2.9 -pydantic-settings>=2.5 -httpx>=0.27 -python-multipart>=0.0.18 -recipe-scrapers>=15.0 -beautifulsoup4>=4.12 -lxml>=5.2 -openai>=1.50 -python-dotenv>=1.0 -aiofiles>=23.0 +# Pinned to the versions verified on the Pi. Bump deliberately, not implicitly: +# an unpinned rebuild months later resolves different versions than the running +# container and breaks in ways the diff cannot explain. +fastapi==0.139.2 +uvicorn==0.51.0 +pydantic==2.13.4 +pydantic-settings==2.14.2 +httpx==0.28.1 +python-multipart==0.0.32 +recipe-scrapers==15.11.0 +beautifulsoup4==4.15.0 +lxml==6.1.1 +openai==2.46.0 +python-dotenv==1.2.2 +aiofiles==25.1.0