MLflow audio artifact logging fails in Docker: missing ffmpeg library

Problem

When running MLflow inside a Docker container, attempts to log or retrieve audio artifacts fail with an exception similar to:


Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/mlflow/tracking/client.py", line 1245, in log_artifact
    _upload_artifact(local_path, artifact_path, run_id, client)
  File "/usr/local/lib/python3.10/site-packages/mlflow/tracking/client.py", line 1103, in _upload_artifact
    _log_artifact(local_path, artifact_path, run_id, client)
  File "/usr/local/lib/python3.10/site-packages/mlflow/tracking/client.py", line 1156, in _log_artifact
    _log_artifact_to_artifact_repo(local_path, artifact_path, run_id, client)
  File "/usr/local/lib/python3.10/site-packages/mlflow/tracking/client.py", line 1172, in _log_artifact_to_artifact_repo
    mlflow.utils.file_utils.copy_file(local_path, dst_path)
  File "/usr/local/lib/python3.10/site-packages/mlflow/utils/file_utils.py", line 95, in copy_file
    shutil.copyfile(src, dst)
FileNotFoundError: [Errno 2] No such file or directory: 'ffmpeg'

The failure prevents:

  • Logging raw .wav, .mp3, or .flac files as artifacts.
  • Automatic conversion of audio to a standardized format performed by MLflow’s mlflow.audio.log_artifact helper.
  • Retrieving the artifact via the UI or mlflow.artifacts.download_artifacts call.

Root Cause

MLflow’s audio utilities rely on the external ffmpeg binary (or the ffprobe companion) to probe, transcode, and validate audio streams. Inside the container the binary is absent, or it is present but not reachable via the PATH environment variable. The missing dependency triggers a FileNotFoundError at the point where the helper invokes subprocess.run(["ffmpeg", …]). The issue is isolated to the container image; the same code runs without error on a host that has ffmpeg installed.

Debug

1. Inspect the container logs


docker logs mlflow-runner
...
mlflow.exceptions.MlflowException: Unable to locate ffmpeg binary. Ensure ffmpeg is installed and on PATH.

2. Verify binary presence


docker exec -it mlflow-runner which ffmpeg
# No output → binary not found

3. Check PATH inside the container


docker exec -it mlflow-runner echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

4. Attempt a manual invocation


docker exec -it mlflow-runner ffmpeg -version
bash: ffmpeg: command not found

5. Identify the base image used


# Dockerfile snippet (original)
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "run.py"]

The python:3.10-slim image does not ship with multimedia libraries.

Solution

Install ffmpeg (and its runtime dependencies) in the Docker image and ensure the binary is on PATH. Below are two approaches: a minimal apt-get install for Debian‑based images and a multi‑stage build that pulls a pre‑compiled static binary.

Approach A – apt‑get installation (Debian/Ubuntu based)


# Dockerfile – before
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "run.py"]

# Dockerfile – after
FROM python:3.10-slim

# Install ffmpeg and its shared libraries
RUN apt-get update && \
    apt-get install -y --no-install-recommends ffmpeg && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "run.py"]

Approach B – static binary (alpine or distroless base)


# Dockerfile – before (alpine)
FROM python:3.10-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "run.py"]

# Dockerfile – after (alpine + static ffmpeg)
FROM python:3.10-alpine

# Download a static build of ffmpeg
ENV FFMPEG_VERSION=6.0
RUN wget -O /tmp/ffmpeg.tar.xz \
    https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-${FFMPEG_VERSION}-amd64-static.tar.xz && \
    tar -xJf /tmp/ffmpeg.tar.xz -C /opt && \
    ln -s /opt/ffmpeg-${FFMPEG_VERSION}-amd64-static/ffmpeg /usr/local/bin/ffmpeg && \
    ln -s /opt/ffmpeg-${FFMPEG_VERSION}-amd64-static/ffprobe /usr/local/bin/ffprobe && \
    rm /tmp/ffmpeg.tar.xz

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "run.py"]

Both approaches place ffmpeg in /usr/local/bin, which is already part of the default PATH.

Verify

1. Rebuild and run the container


docker build -t mlflow-audio .
docker run -d --name mlflow-test -p 5000:5000 mlflow-audio

2. Confirm ffmpeg availability


docker exec -it mlflow-test ffmpeg -version
ffmpeg version 6.0-...
...

3. Execute a minimal logging script


# log_audio.py
import mlflow
import numpy as np
import soundfile as sf
import os

# Generate a 1‑second 440 Hz sine wave
sr = 22050
t = np.linspace(0, 1, sr, False)
tone = 0.5 * np.sin(2 * np.pi * 440 * t)
os.makedirs("tmp", exist_ok=True)
sf.write("tmp/sine.wav", tone, sr)

with mlflow.start_run():
    mlflow.log_artifact("tmp/sine.wav", artifact_path="audio")
    print("Artifact logged")

docker exec -it mlflow-test python log_audio.py
Artifact logged

4. Verify artifact presence via the MLflow UI or CLI


mlflow artifacts list -r 
audio/sine.wav

Prevent

  • Base‑image selection: Prefer images that already contain multimedia utilities (e.g., python:3.10-slim-bullseye with ffmpeg added) to avoid ad‑hoc installs.
  • Dockerfile linting: Enforce a rule that ffmpeg must be present when the mlflow.audio module is imported. CI can run docker run … which ffmpeg as a sanity check.
  • Health‑check: Add a container health‑check that runs ffmpeg -version and fails if the exit code is non‑zero.
  • Version pinning: Install a specific ffmpeg version to guarantee consistent codec support across environments.
  • Documentation: Clearly state the runtime dependency in the project’s README and in the requirements.txt (as a comment) so new developers know to include it in custom images.

Related Topic Hub: Model Serving Troubleshooting Hub

FAQ

  1. Why does mlflow.audio.log_artifact work locally but not in CI?
    CI pipelines often use minimal base images that lack ffmpeg. The helper calls subprocess.run(["ffmpeg", …]), which fails when the binary is missing.
  2. Can I use a pure‑Python library instead of ffmpeg?
    MLflow delegates audio probing to ffprobe for reliable codec detection. Replacing it would require modifying MLflow’s source; installing the binary is the supported path.
  3. Is ffmpeg required for non‑audio artifacts?
    No. Only the mlflow.audio module triggers the dependency. Other artifact types (e.g., model files, CSVs) do not need it.
  4. What if I need a specific codec that the default ffmpeg package lacks?
    Install the full ffmpeg package (not the -ffmpeg minimal variant) or compile a custom static binary with the required codec enabled, then replace the binary in the image.
  5. How do I debug a “ffprobe not found” error?
    Run docker exec which ffprobe. If missing, add ffprobe to the image (it ships with the same package as ffmpeg on Debian/Ubuntu). Verify that both binaries are on PATH and that the container’s LD_LIBRARY_PATH includes any required shared libraries.