/
/
1# syntax=docker/dockerfile:1
2
3# Python version is centralized in .python-version at repo root.
4# Workflows read that file and pass it here via --build-arg PYTHON_VERSION=...
5# The default below is a safety net for manual `docker build` without --build-arg.
6ARG PYTHON_VERSION=3.14
7
8# BASE docker image for music assistant container
9# This image forms the base for the final image and is not meant to be used directly
10# NOTE that the dev add-on is also based on this base image
11
12FROM python:${PYTHON_VERSION}-slim-trixie AS ffmpeg-builder
13
14# Enable non-free and contrib repositories for FDK-AAC and other codecs
15# (trixie uses the deb822 sources format)
16RUN sed -i 's/^Components: .*/Components: main contrib non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources
17
18# Install build dependencies for FFmpeg
19RUN apt-get update && apt-get install -y --no-install-recommends \
20 build-essential \
21 pkg-config \
22 yasm \
23 nasm \
24 git \
25 wget \
26 ca-certificates \
27 # Audio codec libraries
28 libfdk-aac-dev \
29 libmp3lame-dev \
30 libopus-dev \
31 libvorbis-dev \
32 libsoxr-dev \
33 libspeex-dev \
34 libtwolame-dev \
35 libvo-amrwbenc-dev \
36 libopencore-amrnb-dev \
37 libopencore-amrwb-dev \
38 libshine-dev \
39 # Audio processing and filters
40 librubberband-dev \
41 libbs2b-dev \
42 libsamplerate0-dev \
43 libmysofa-dev \
44 libjack-jackd2-dev \
45 libpulse-dev \
46 # Additional libraries
47 libbluray-dev \
48 libxml2-dev \
49 libssh-dev \
50 liblzma-dev \
51 # SSL/TLS support for HTTPS
52 libssl-dev \
53 && rm -rf /var/lib/apt/lists/*
54
55# Build FFmpeg 7.1.2 from source with comprehensive audio codec support
56ARG FFMPEG_VERSION=7.1.2
57RUN set -x \
58 && wget -q "https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz" -O /tmp/ffmpeg.tar.xz \
59 && tar -xJf /tmp/ffmpeg.tar.xz -C /tmp \
60 && cd /tmp/ffmpeg-${FFMPEG_VERSION} \
61 && ./configure \
62 --prefix=/usr/local \
63 --enable-gpl \
64 --enable-nonfree \
65 --enable-version3 \
66 # Audio codecs (comprehensive support)
67 --enable-libfdk-aac \
68 --enable-libmp3lame \
69 --enable-libopus \
70 --enable-libvorbis \
71 --enable-libspeex \
72 --enable-libtwolame \
73 --enable-libshine \
74 --enable-libopencore-amrnb \
75 --enable-libopencore-amrwb \
76 --enable-libvo-amrwbenc \
77 # Audio filters and resampling
78 --enable-libsoxr \
79 --enable-librubberband \
80 --enable-libbs2b \
81 --enable-libmysofa \
82 --enable-libjack \
83 --enable-libpulse \
84 # SSL/TLS support for HTTPS
85 --enable-openssl \
86 # Additional libraries for playlist and network support
87 --enable-libxml2 \
88 --enable-libssh \
89 --enable-lzma \
90 # Optimizations
91 --enable-runtime-cpudetect \
92 # Disable unnecessary features for smaller build
93 --disable-doc \
94 --disable-htmlpages \
95 --disable-manpages \
96 --disable-podpages \
97 --disable-txtpages \
98 --disable-debug \
99 --disable-static \
100 --enable-shared \
101 && make -j$(nproc) \
102 && make install \
103 && strip /usr/local/bin/ffmpeg /usr/local/bin/ffprobe \
104 && rm -rf /tmp/ffmpeg*
105
106##################################################################################################
107
108# PyAV wheel builder - builds PyAV against the custom FFmpeg
109FROM python:${PYTHON_VERSION}-slim-trixie AS pyav-builder
110
111RUN apt-get update && apt-get install -y --no-install-recommends \
112 gcc g++ python3-dev pkg-config && rm -rf /var/lib/apt/lists/*
113
114COPY --from=ffmpeg-builder /usr/local/lib/libav*.so* /usr/local/lib/
115COPY --from=ffmpeg-builder /usr/local/lib/libsw*.so* /usr/local/lib/
116COPY --from=ffmpeg-builder /usr/local/lib/libpostproc.so* /usr/local/lib/
117COPY --from=ffmpeg-builder /usr/local/include/ /usr/local/include/
118COPY --from=ffmpeg-builder /usr/local/lib/pkgconfig/ /usr/local/lib/pkgconfig/
119
120RUN ldconfig
121ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
122
123# Build PyAV wheel against the custom FFmpeg with the in the Sendspin provider manifest pinned version
124COPY music_assistant/providers/sendspin/manifest.json /tmp/sendspin_manifest.json
125RUN PYAV_VERSION=$(python -c "import json; reqs=json.load(open('/tmp/sendspin_manifest.json'))['requirements']; print(next(r.split('==')[1] for r in reqs if r.startswith('av==')))") && \
126 echo "Building PyAV version: ${PYAV_VERSION}" && \
127 pip wheel --no-binary av av==${PYAV_VERSION} -w /wheels/
128
129##################################################################################################
130
131# Build shairport-sync (airplay receiver provider) from source against this image's
132# libraries, using the same configuration as music_assistant/providers/airplay_receiver/bin/build_binaries.sh:
133# tinysvcmdns as embedded mDNS backend (no avahi), stdout/pipe outputs and metadata support.
134FROM python:${PYTHON_VERSION}-slim-trixie AS shairport-builder
135
136RUN apt-get update && apt-get install -y --no-install-recommends \
137 build-essential \
138 git \
139 autoconf \
140 automake \
141 libtool \
142 pkg-config \
143 libconfig-dev \
144 libpopt-dev \
145 libssl-dev \
146 libdbus-1-dev \
147 libglib2.0-dev \
148 ca-certificates \
149 && rm -rf /var/lib/apt/lists/*
150
151# Version and its commit SHA are the single source of truth (also read by
152# build_binaries.sh). Tags are mutable, so the fetched tag is verified against the
153# pinned SHA. Bump both when changing the version.
154ARG SHAIRPORT_VERSION=4.3.7
155ARG SHAIRPORT_SHA=0b1c4391ffd398e7b145eb4b98416261380adeea
156RUN set -x \
157 && git init /tmp/shairport-sync \
158 && cd /tmp/shairport-sync \
159 && git remote add origin https://github.com/mikebrady/shairport-sync.git \
160 && git fetch --depth 1 origin refs/tags/${SHAIRPORT_VERSION} \
161 && git checkout FETCH_HEAD \
162 && test "$(git rev-parse HEAD)" = "${SHAIRPORT_SHA}" \
163 && autoreconf -fi \
164 && ./configure \
165 --with-pipe \
166 --with-metadata \
167 --without-avahi \
168 --without-dns-sd \
169 --with-tinysvcmdns \
170 --with-ssl=openssl \
171 --with-stdout \
172 --sysconfdir=/etc \
173 && make -j$(nproc) \
174 && strip shairport-sync \
175 && cp shairport-sync /usr/local/bin/shairport-sync \
176 && rm -rf /tmp/shairport-sync
177
178##################################################################################################
179
180FROM python:${PYTHON_VERSION}-slim-trixie
181
182# Ensure UTF-8 encoding across system
183ENV LANG=C.UTF-8
184
185# Enable non-free and contrib repositories for codec libraries
186# (trixie uses the deb822 sources format)
187RUN sed -i 's/^Components: .*/Components: main contrib non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources
188
189# Install runtime dependencies
190RUN set -x \
191 && apt-get update \
192 && apt-get install -y --no-install-recommends \
193 ca-certificates \
194 libjemalloc2 \
195 tzdata \
196 wget \
197 # cifs utils and libnfs are needed for smb and nfs support (file provider)
198 cifs-utils \
199 libnfs14 \
200 nfs-common \
201 # airplay libraries
202 openssl \
203 libssl-dev \
204 libuuid1 \
205 libcurl4 \
206 libsodium23 \
207 libconfuse2 \
208 libevent-dev \
209 libjson-c5 \
210 libgcrypt20 \
211 # glib is needed by the shairport-sync binary (previously pulled in only
212 # indirectly via the pipewire/pulseaudio utils)
213 libglib2.0-0 \
214 # libsndfile needed for librosa audio file support (smartfades)
215 libsndfile1 \
216 # libchromaprint needed for AcoustID fingerprinting (acoustid_lookup)
217 libchromaprint1 \
218 # Audio codec runtime libraries (needed for FFmpeg)
219 libfdk-aac2 \
220 libmp3lame0 \
221 libopus0 \
222 libvorbis0a \
223 libvorbisenc2 \
224 libsoxr0 \
225 libspeex1 \
226 libtwolame0 \
227 libshine3 \
228 libopencore-amrnb0 \
229 libopencore-amrwb0 \
230 libvo-amrwbenc0 \
231 # Audio processing libraries
232 librubberband2 \
233 libbs2b0 \
234 libsamplerate0 \
235 libmysofa1 \
236 libjack-jackd2-0 \
237 libpulse0 \
238 # Additional libraries
239 libbluray2 \
240 libxml2 \
241 libssh-4 \
242 liblzma5 \
243 # Snapcast dependencies
244 libasound2 \
245 # PortAudio for local audio output (sounddevice)
246 libportaudio2 \
247 # PipeWire stack (daemon + libs) required by the pipewire-alsa plugin
248 # unpacked below (host pipewire access in docker, local audio)
249 pipewire \
250 # Allows pulse audio devices to be detected using pactl. (local audio)
251 pulseaudio-utils \
252 # PulseAudio daemon for MA's private audio-capture server (spotify connect soloist)
253 pulseaudio \
254 libvorbisidec1 \
255 libflac14 \
256 libavahi-client3 \
257 libavahi-common3 \
258 # AirPlay receiver dependencies (shairport-sync)
259 libconfig11 \
260 libpopt0 \
261 # pipewire-alsa (ALSA apps -> host pipewire, needed for local audio in docker)
262 # declares Conflicts: pulseaudio because both want to own the ALSA default
263 # device, but there is no file overlap, so unpack it outside dpkg and keep
264 # the ALSA default routed to pipewire by removing pulseaudio's
265 # default-routing and autospawn snippets.
266 # NOTE: the unpacked package is not registered in dpkg, so package/CVE
267 # scanners will not list pipewire-alsa.
268 # This whole workaround (and the pipewire package above) exists only for
269 # the local audio provider and can be dropped once that moves to its own
270 # dedicated container, leaving a plain apt install of pulseaudio.
271 && (cd /tmp && apt-get download pipewire-alsa) \
272 && dpkg-deb -x /tmp/pipewire-alsa_*.deb / \
273 && rm /tmp/pipewire-alsa_*.deb \
274 && rm -f \
275 /etc/alsa/conf.d/99-pulse.conf \
276 /usr/share/alsa/alsa.conf.d/pulse.conf \
277 /etc/pulse/client.conf.d/01-enable-autospawn.conf \
278 && apt-get clean \
279 && rm -rf /var/lib/apt/lists/* \
280 && rm -f \
281 /usr/bin/parecord \
282 /usr/bin/paplay \
283 /usr/bin/parec \
284 /usr/bin/pamon \
285 /usr/bin/pacat \
286 /usr/bin/pasuspender \
287 /usr/bin/pacmd
288
289# Install Snapcast 0.34.0 from GitHub releases (requires at least 0.27)
290ARG SNAPCAST_VERSION=0.34.0
291ARG TARGETARCH
292RUN set -x \
293 && if [ "$TARGETARCH" = "arm64" ]; then \
294 SNAPCAST_ARCH="arm64"; \
295 else \
296 SNAPCAST_ARCH="amd64"; \
297 fi \
298 && wget -q "https://github.com/badaix/snapcast/releases/download/v${SNAPCAST_VERSION}/snapserver_${SNAPCAST_VERSION}-1_${SNAPCAST_ARCH}_trixie.deb" -O /tmp/snapserver.deb \
299 && wget -q "https://github.com/badaix/snapcast/releases/download/v${SNAPCAST_VERSION}/snapclient_${SNAPCAST_VERSION}-1_${SNAPCAST_ARCH}_trixie.deb" -O /tmp/snapclient.deb \
300 # install via apt so any missing dependencies of the debs are resolved as well
301 && apt-get update \
302 && apt-get install -y --no-install-recommends /tmp/snapserver.deb /tmp/snapclient.deb \
303 && apt-get clean \
304 && rm -rf /var/lib/apt/lists/* \
305 && rm /tmp/snapserver.deb /tmp/snapclient.deb
306
307# Install go-librespot (used by the Spotify Connect provider) from GitHub releases.
308# The release binaries statically link libvorbis/libogg/libFLAC, so the only dynamic
309# dependency is libasound2 (already installed above for Snapcast).
310# The pinned SHA256 per arch is verified before extraction; bump both when changing the version.
311ARG GO_LIBRESPOT_VERSION=0.9.0
312RUN set -x \
313 && if [ "$TARGETARCH" = "arm64" ]; then \
314 GO_LIBRESPOT_ARCH="arm64"; \
315 GO_LIBRESPOT_SHA256="79b80bb3723b7973165d2d94c428676b8582780aeca7c54694589206ab741e91"; \
316 else \
317 GO_LIBRESPOT_ARCH="x86_64"; \
318 GO_LIBRESPOT_SHA256="87b27ce57cc7871bad6ffac8acba7e2a89c72a7e6c7990b88bec404f449f381e"; \
319 fi \
320 && wget -q "https://github.com/devgianlu/go-librespot/releases/download/v${GO_LIBRESPOT_VERSION}/go-librespot_linux_${GO_LIBRESPOT_ARCH}.tar.gz" -O /tmp/go-librespot.tar.gz \
321 && echo "${GO_LIBRESPOT_SHA256} /tmp/go-librespot.tar.gz" | sha256sum -c - \
322 && tar -xzf /tmp/go-librespot.tar.gz -C /usr/local/bin go-librespot \
323 && chmod +x /usr/local/bin/go-librespot \
324 && rm /tmp/go-librespot.tar.gz
325
326# Copy FFmpeg binaries and libraries from builder stage
327COPY --from=ffmpeg-builder /usr/local/bin/ffmpeg /usr/local/bin/
328COPY --from=ffmpeg-builder /usr/local/bin/ffprobe /usr/local/bin/
329COPY --from=ffmpeg-builder /usr/local/lib/libav*.so* /usr/local/lib/
330COPY --from=ffmpeg-builder /usr/local/lib/libsw*.so* /usr/local/lib/
331COPY --from=ffmpeg-builder /usr/local/lib/libpostproc.so* /usr/local/lib/
332
333# Copy shairport-sync binary from builder stage (found by the airplay receiver
334# provider via its system PATH lookup)
335COPY --from=shairport-builder /usr/local/bin/shairport-sync /usr/local/bin/
336
337# Copy pre-built PyAV wheel for use by downstream images
338COPY --from=pyav-builder /wheels/ /usr/local/share/pyav-wheels/
339
340# Update shared library cache and verify FFmpeg
341RUN ldconfig && ffmpeg -version && ffprobe -version
342
343# Copy widevine client files to container
344RUN mkdir -p /usr/local/bin/widevine_cdm
345COPY widevine_cdm/* /usr/local/bin/widevine_cdm/
346
347# we need to set (very permissive) permissions to the workdir
348# and /tmp to allow running the container as non-root
349RUN chmod -R 777 /tmp
350
351LABEL \
352 org.opencontainers.image.title="Music Assistant Base Image" \
353 org.opencontainers.image.description="Base Image for Music Assistant server - not to be used directly" \
354 org.opencontainers.image.source="https://github.com/music-assistant/server" \
355 org.opencontainers.image.authors="The Music Assistant Team" \
356 org.opencontainers.image.licenses="Apache License 2.0"
357